diff options
author | Erik Verbruggen <erik.verbruggen@qt.io> | 2018-10-09 14:58:01 +0200 |
---|---|---|
committer | Erik Verbruggen <erik.verbruggen@qt.io> | 2019-01-25 10:26:13 +0000 |
commit | 784a55a15ddc65b59cc4709e54453238438eae48 (patch) | |
tree | 2903e0c690a8aef0b49f3e5d6c26ef4ac90543aa /src | |
parent | 923fef3ad3076e337eba4e603a6f759c54cc404c (diff) |
V4: Collect trace information in the interpreter
Collect type information about values used in a function. These include
all parameters, and the results of many bytecode instructions. For array
loads/stores, it also tracks if the access is in-bounds of a
SimpleArrayData.
Collection is only enabled when the qml-tracing feature is turned on
while configuring.
In subsequent patches this is used to generated optimized JITted code.
Change-Id: I63985c334c3fdc55fca7fb4addfe3e535989aac5
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src')
26 files changed, 619 insertions, 208 deletions
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index 4f3dc27acc..5a27d3948c 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -51,6 +51,7 @@ // We mean it. // #include <private/qv4instr_moth_p.h> +#include <private/qv4compileddata_p.h> QT_BEGIN_NAMESPACE @@ -65,6 +66,8 @@ namespace Moth { class BytecodeGenerator { public: + typedef CompiledData::Function::TraceInfoCount TraceInfoCount; + BytecodeGenerator(int line, bool debug) : startLine(line), debugMode(debug) {} @@ -161,6 +164,15 @@ 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 @@ -172,14 +184,12 @@ QT_WARNING_POP Q_REQUIRED_RESULT Jump jumpTrue() { - Instruction::JumpTrue data; - return addJumpInstruction(data); + return addTracingJumpInstruction(Instruction::JumpTrue()); } Q_REQUIRED_RESULT Jump jumpFalse() { - Instruction::JumpFalse data; - return addJumpInstruction(data); + return addTracingJumpInstruction(Instruction::JumpFalse()); } Q_REQUIRED_RESULT Jump jumpNotUndefined() @@ -198,16 +208,16 @@ QT_WARNING_POP { Instruction::CmpStrictEqual cmp; cmp.lhs = lhs; - addInstruction(cmp); - addJumpInstruction(Instruction::JumpTrue()).link(target); + addInstruction(std::move(cmp)); + addTracingJumpInstruction(Instruction::JumpTrue()).link(target); } void jumpStrictNotEqual(const StackSlot &lhs, const Label &target) { Instruction::CmpStrictNotEqual cmp; cmp.lhs = lhs; - addInstruction(cmp); - addJumpInstruction(Instruction::JumpTrue()).link(target); + addInstruction(std::move(cmp)); + addTracingJumpInstruction(Instruction::JumpTrue()).link(target); } void setUnwindHandler(ExceptionHandler *handler) @@ -248,6 +258,13 @@ 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; @@ -258,9 +275,9 @@ QT_WARNING_POP void addCJumpInstruction(bool jumpOnFalse, const Label *trueLabel, const Label *falseLabel) { if (jumpOnFalse) - addJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); + addTracingJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); else - addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); + addTracingJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); } void clearLastInstruction() @@ -268,6 +285,27 @@ 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; + } + private: friend struct Jump; friend struct Label; @@ -302,6 +340,8 @@ private: int lastInstrType = -1; Moth::Instr lastInstr; + + TraceInfoCount nTraceInfos = TraceInfoCount(0); }; } diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp index af86b70014..1508790926 100644 --- a/src/qml/compiler/qv4bytecodehandler.cpp +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -95,6 +95,12 @@ void ByteCodeHandler::decode(const char *code, uint len) Q_UNUSED(arg2); \ Q_UNUSED(arg3); \ Q_UNUSED(arg4); +#define MOTH_UNUSED_ARGS5(arg1, arg2, arg3, arg4, arg5) \ + Q_UNUSED(arg1); \ + Q_UNUSED(arg2); \ + Q_UNUSED(arg3); \ + Q_UNUSED(arg4); \ + Q_UNUSED(arg5); #define MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, ...) \ MOTH_EXPAND_FOR_MSVC(MOTH_UNUSED_ARGS##nargs(__VA_ARGS__)) diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h index ca6abf3dc3..b37c8810bd 100644 --- a/src/qml/compiler/qv4bytecodehandler_p.h +++ b/src/qml/compiler/qv4bytecodehandler_p.h @@ -75,6 +75,12 @@ namespace Moth { int arg2, \ int arg3, \ int arg4 +#define BYTECODE_HANDLER_DEFINE_ARGS5(arg1, arg2, arg3, arg4, arg5) \ + int arg1, \ + int arg2, \ + int arg3, \ + int arg4, \ + int arg5 #define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER_INSTRUCTION(name, nargs, ...) \ virtual void generate_##name( \ diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index cdba21604d..6ad26c41dd 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -287,8 +287,8 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) switch (op) { case UMinus: { expr.loadInAccumulator(); - Instruction::UMinus uminus; - bytecodeGenerator->addInstruction(uminus); + Instruction::UMinus uminus = {}; + bytecodeGenerator->addTracingInstruction(uminus); return Reference::fromAccumulator(this); } case UPlus: { @@ -316,8 +316,8 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Instruction::UPlus uplus; bytecodeGenerator->addInstruction(uplus); Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); - Instruction::Increment inc; - bytecodeGenerator->addInstruction(inc); + Instruction::Increment inc = {}; + bytecodeGenerator->addTracingInstruction(inc); e.storeConsumeAccumulator(); return originalValue; } else { @@ -328,8 +328,8 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) case PreIncrement: { Reference e = expr.asLValue(); e.loadInAccumulator(); - Instruction::Increment inc; - bytecodeGenerator->addInstruction(inc); + Instruction::Increment inc = {}; + bytecodeGenerator->addTracingInstruction(inc); if (_expr.accept(nx)) return e.storeConsumeAccumulator(); else @@ -342,8 +342,8 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Instruction::UPlus uplus; bytecodeGenerator->addInstruction(uplus); Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); - Instruction::Decrement dec; - bytecodeGenerator->addInstruction(dec); + Instruction::Decrement dec = {}; + bytecodeGenerator->addTracingInstruction(dec); e.storeConsumeAccumulator(); return originalValue; } else { @@ -354,8 +354,8 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) case PreDecrement: { Reference e = expr.asLValue(); e.loadInAccumulator(); - Instruction::Decrement dec; - bytecodeGenerator->addInstruction(dec); + Instruction::Decrement dec = {}; + bytecodeGenerator->addTracingInstruction(dec); if (_expr.accept(nx)) return e.storeConsumeAccumulator(); else @@ -1179,8 +1179,8 @@ bool Codegen::visit(ArrayPattern *ast) slot.storeConsumeAccumulator(); index.loadInAccumulator(); - Instruction::Increment inc; - bytecodeGenerator->addInstruction(inc); + Instruction::Increment inc = {}; + bytecodeGenerator->addTracingInstruction(inc); index.storeConsumeAccumulator(); }; @@ -1243,7 +1243,7 @@ bool Codegen::visit(ArrayPattern *ast) next.value = lhsValue.stackSlot(); next.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(next); - bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body); + bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpFalse()).link(body); bytecodeGenerator->jump().link(done); end.link(); @@ -1527,24 +1527,24 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re { switch (oper) { case QSOperator::Add: { - //### Todo: when we add type hints, we can generate an Increment when both the lhs is a number and the rhs == 1 left = left.storeOnStack(); right.loadInAccumulator(); Instruction::Add add; add.lhs = left.stackSlot(); - bytecodeGenerator->addInstruction(add); + bytecodeGenerator->addTracingInstruction(add); break; } case QSOperator::Sub: { if (right.isConstant() && right.constant == Encode(int(1))) { left.loadInAccumulator(); - bytecodeGenerator->addInstruction(Instruction::Decrement()); + Instruction::Decrement dec = {}; + bytecodeGenerator->addTracingInstruction(dec); } else { left = left.storeOnStack(); right.loadInAccumulator(); Instruction::Sub sub; sub.lhs = left.stackSlot(); - bytecodeGenerator->addInstruction(sub); + bytecodeGenerator->addTracingInstruction(sub); } break; } @@ -1561,7 +1561,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Mul mul; mul.lhs = left.stackSlot(); - bytecodeGenerator->addInstruction(mul); + bytecodeGenerator->addTracingInstruction(mul); break; } case QSOperator::Div: { @@ -1577,7 +1577,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Mod mod; mod.lhs = left.stackSlot(); - bytecodeGenerator->addInstruction(mod); + bytecodeGenerator->addTracingInstruction(mod); break; } case QSOperator::BitAnd: @@ -1961,7 +1961,7 @@ bool Codegen::visit(CallExpression *ast) call.thisObject = baseObject.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else { Instruction::TailCall call; call.func = base.stackSlot(); @@ -1989,14 +1989,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.name = base.qmlCoreIndex; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else if (base.type == Reference::QmlContextObject) { Instruction::CallContextObjectProperty call; call.base = base.qmlBase.stackSlot(); call.name = base.qmlCoreIndex; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else if (base.type == Reference::Member) { if (!disable_lookups && useFastLookups) { Instruction::CallPropertyLookup call; @@ -2004,14 +2004,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.lookupIndex = registerGetterLookup(base.propertyNameIndex); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else { Instruction::CallProperty call; call.base = base.propertyBase.stackSlot(); call.name = base.propertyNameIndex; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } } else if (base.type == Reference::Subscript) { Instruction::CallElement call; @@ -2019,25 +2019,25 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.index = base.elementSubscript.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else if (base.type == Reference::Name) { if (base.name == QStringLiteral("eval")) { Instruction::CallPossiblyDirectEval call; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else if (!disable_lookups && useFastLookups && base.global) { Instruction::CallGlobalLookup call; call.index = registerGlobalGetterLookup(base.nameAsIndex()); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else { Instruction::CallName call; call.name = base.nameAsIndex(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } } else if (base.type == Reference::SuperProperty) { Reference receiver = base.baseObject(); @@ -2054,14 +2054,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.thisObject = receiver.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else { Q_ASSERT(base.isStackSlot()); Instruction::CallValue call; call.name = base.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } _expr.setResult(Reference::fromAccumulator(this)); @@ -2805,14 +2805,14 @@ bool Codegen::visit(TemplateLiteral *ast) Instruction::Add instr; instr.lhs = temp2; - bytecodeGenerator->addInstruction(instr); + bytecodeGenerator->addTracingInstruction(instr); } else { expr.loadInAccumulator(); } Instruction::Add instr; instr.lhs = temp; - bytecodeGenerator->addInstruction(instr); + bytecodeGenerator->addTracingInstruction(instr); } auto r = Reference::fromAccumulator(this); @@ -3070,6 +3070,7 @@ 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()); @@ -3158,6 +3159,7 @@ 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 @@ -3393,7 +3395,7 @@ bool Codegen::visit(ForEachStatement *ast) next.value = lhsValue.stackSlot(); next.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(next); - bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body); + bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpFalse()).link(body); bytecodeGenerator->jump().link(done); end.link(); @@ -4281,7 +4283,7 @@ void Codegen::Reference::storeAccumulator() const Instruction::StoreElement store; store.base = elementBase; store.index = elementSubscript.stackSlot(); - codegen->bytecodeGenerator->addInstruction(store); + codegen->bytecodeGenerator->addTracingInstruction(store); } return; case QmlScopeObject: { Instruction::StoreScopeObjectProperty store; @@ -4383,12 +4385,12 @@ QT_WARNING_POP if (!scope) { Instruction::LoadLocal load; load.index = index; - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } else { Instruction::LoadScopedLocal load; load.index = index; load.scope = scope; - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } tdzCheck(requiresTDZCheck); return; @@ -4411,11 +4413,11 @@ QT_WARNING_POP if (!disable_lookups && global) { Instruction::LoadGlobalLookup load; load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } else { Instruction::LoadName load; load.name = nameAsIndex(); - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } return; case Member: @@ -4424,11 +4426,11 @@ QT_WARNING_POP if (!disable_lookups && codegen->useFastLookups) { Instruction::GetLookup load; load.index = codegen->registerGetterLookup(propertyNameIndex); - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } else { Instruction::LoadProperty load; load.name = propertyNameIndex; - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } return; case Import: { @@ -4443,7 +4445,7 @@ QT_WARNING_POP tdzCheck(subscriptRequiresTDZCheck); Instruction::LoadElement load; load.base = elementBase; - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } return; case QmlScopeObject: { Instruction::LoadScopeObjectProperty load; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index a69e862fb7..c90bbf03e6 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -188,7 +188,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) runtimeFunctions.resize(data->functionTableSize); for (int i = 0 ;i < runtimeFunctions.size(); ++i) { const QV4::CompiledData::Function *compiledFunction = data->functionAt(i); - runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction); + runtimeFunctions[i] = QV4::Function::create(engine, this, compiledFunction); } Scope scope(engine); @@ -287,7 +287,8 @@ void CompilationUnit::unlink() runtimeRegularExpressions = nullptr; free(runtimeClasses); runtimeClasses = nullptr; - qDeleteAll(runtimeFunctions); + for (QV4::Function *f : qAsConst(runtimeFunctions)) + f->destroy(); runtimeFunctions.clear(); } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 5732855cc9..52c8e9f651 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -73,7 +73,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x19 +#define QV4_DATA_STRUCTURE_VERSION 0x1a class QIODevice; class QQmlPropertyCache; @@ -299,6 +299,9 @@ struct Function size_t dependingScopePropertiesOffset() const { return dependingContextPropertiesOffset() + nDependingContextProperties * sizeof(quint32); } // Qml Extensions End + typedef quint16_le TraceInfoCount; + TraceInfoCount nTraceInfos; + static constexpr TraceInfoCount NoTracing() { return TraceInfoCount::max(); } // Keep all unaligned data at the end quint8 flags; quint8 padding1; @@ -334,7 +337,7 @@ struct Function return (a + 7) & ~size_t(7); } }; -static_assert(sizeof(Function) == 52, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Function) == 56, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Method { enum Type { diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 3076c6b526..8735cc074b 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -420,7 +420,7 @@ 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; function->nDependingIdObjects = 0; diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index 5772bff7bf..419c77fc03 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -407,4 +407,28 @@ 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); + + //### the next condition should be refined and have the IR distinguish between escaping and + // non-escaping locals + return !hasTry && !requiresExecutionContext && !hasNestedFunctions; +#else + return false; +#endif +} + QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 328715da07..0fa1074580 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -162,6 +162,7 @@ struct Context { int line = 0; int column = 0; int registerCountInFunction = 0; + uint nTraceInfos = 0; int functionIndex = -1; int blockIndex = -1; @@ -366,6 +367,8 @@ 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 6edf5a4ae7..def58de9a8 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -156,7 +156,7 @@ QString dumpRegister(int reg, int nFormals) return QStringLiteral("(this)"); else if (reg == CallData::Argc) return QStringLiteral("(argc)"); - reg -= CallData::OffsetCount; + reg -= CallData::HeaderSize(); if (reg <= nFormals) return QStringLiteral("a%1").arg(reg); reg -= nFormals; @@ -171,6 +171,7 @@ 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) { @@ -240,9 +241,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(LoadLocal) if (index < nLocals) - d << "l" << index; + d << "l" << index << TRACE_SLOT; else - d << "a" << (index - nLocals); + d << "a" << (index - nLocals) << TRACE_SLOT; MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) @@ -254,9 +255,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; + d << "l" << index << "@" << scope << TRACE_SLOT; else - d << "a" << (index - nLocals) << "@" << scope; + d << "a" << (index - nLocals) << "@" << scope << TRACE_SLOT; MOTH_END_INSTR(LoadScopedLocal) MOTH_BEGIN_INSTR(StoreScopedLocal) @@ -279,11 +280,11 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) - d << name; + d << name << TRACE_SLOT; MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) - d << index; + d << index << TRACE_SLOT; MOTH_END_INSTR(LoadGlobalLookup) MOTH_BEGIN_INSTR(StoreNameSloppy) @@ -295,19 +296,20 @@ 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]"; + d << dumpRegister(base, nFormals) << "[acc]" << TRACE_SLOT; MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) - d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" + << TRACE_SLOT; MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) - d << "acc[" << name << "]"; + d << "acc[" << name << "]" << TRACE_SLOT; MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) - d << "acc(" << index << ")"; + d << "acc(" << index << ")" << TRACE_SLOT; MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) @@ -357,55 +359,63 @@ 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); + d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallWithReceiver) - d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals); + d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals) + << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallWithReceiver) MOTH_BEGIN_INSTR(CallProperty) - d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals); + 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); + d << dumpRegister(base, nFormals) << "." << lookupIndex + << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallElement) - d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" << dumpArguments(argc, argv, nFormals); + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" + << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallName) - d << name << dumpArguments(argc, argv, nFormals); + d << name << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallName) MOTH_BEGIN_INSTR(CallPossiblyDirectEval) - d << dumpArguments(argc, argv, nFormals); + d << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) - d << index << dumpArguments(argc, argv, nFormals); + d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(CallScopeObjectProperty) - d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals); + d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals) + << TRACE_SLOT; MOTH_END_INSTR(CallScopeObjectProperty) MOTH_BEGIN_INSTR(CallContextObjectProperty) - d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals); + d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals) + << TRACE_SLOT; MOTH_END_INSTR(CallContextObjectProperty) MOTH_BEGIN_INSTR(CallWithSpread) - d << "new" << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals); + d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) + << dumpArguments(argc, argv, nFormals) + << TRACE_SLOT; MOTH_END_INSTR(CallWithSpread) MOTH_BEGIN_INSTR(Construct) - d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); + d << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(Construct) MOTH_BEGIN_INSTR(ConstructWithSpread) - d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); + d << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(ConstructWithSpread) MOTH_BEGIN_INSTR(SetUnwindHandler) @@ -540,11 +550,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(); + d << ABSOLUTE_OFFSET() << TRACE_SLOT; MOTH_END_INSTR(JumpTrue) MOTH_BEGIN_INSTR(JumpFalse) - d << ABSOLUTE_OFFSET(); + d << ABSOLUTE_OFFSET() << TRACE_SLOT; MOTH_END_INSTR(JumpFalse) MOTH_BEGIN_INSTR(JumpNotUndefined) @@ -608,19 +618,22 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st 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) - MOTH_END_INSTR(PreIncrement) + d << TRACE_SLOT; + MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) - MOTH_END_INSTR(PreDecrement) + d << TRACE_SLOT; + MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Add) - d << dumpRegister(lhs, nFormals) << ", acc"; + d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(BitAnd) @@ -676,7 +689,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"; + d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; MOTH_END_INSTR(Mul) MOTH_BEGIN_INSTR(Div) @@ -684,11 +697,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"; + d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; MOTH_END_INSTR(Mod) MOTH_BEGIN_INSTR(Sub) - d << dumpRegister(lhs, nFormals) << ", acc"; + d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; 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 2ca8f692b8..080f4b42b6 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -77,19 +77,19 @@ 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, 1, index) +#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 2, index, traceSlot) #define INSTR_StoreLocal(op) INSTRUCTION(op, StoreLocal, 1, index) -#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 2, scope, index) +#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 3, scope, index, traceSlot) #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, 1, name) -#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index) +#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 2, name, traceSlot) +#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 2, index, traceSlot) #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, 1, name) -#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 1, index) +#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, traceSlot) +#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, traceSlot) #define INSTR_LoadScopeObjectProperty(op) INSTRUCTION(op, LoadScopeObjectProperty, 3, propertyIndex, base, captureRequired) #define INSTR_LoadContextObjectProperty(op) INSTRUCTION(op, LoadContextObjectProperty, 3, propertyIndex, base, captureRequired) #define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base) @@ -103,19 +103,19 @@ QT_BEGIN_NAMESPACE #define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property) #define INSTR_StoreScopeObjectProperty(op) INSTRUCTION(op, StoreScopeObjectProperty, 2, base, propertyIndex) #define INSTR_StoreContextObjectProperty(op) INSTRUCTION(op, StoreContextObjectProperty, 2, base, propertyIndex) -#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_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 4, name, base, argc, argv) -#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 4, name, base, argc, argv) -#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 4, func, thisObject, argc, argv) +#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_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 5, name, base, argc, argv, traceSlot) +#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 5, name, base, argc, argv, traceSlot) +#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 5, func, thisObject, argc, argv, traceSlot) #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) @@ -152,8 +152,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, 1, offset) -#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset) +#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 2, traceSlot, offset) +#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 2, traceSlot, 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) @@ -172,11 +172,11 @@ QT_BEGIN_NAMESPACE #define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs) #define INSTR_UNot(op) INSTRUCTION(op, UNot, 0) #define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0) -#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0) +#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 1, traceSlot) #define INSTR_UCompl(op) INSTRUCTION(op, UCompl, 0) -#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_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_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) @@ -190,10 +190,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, 1, lhs) +#define INSTR_Mul(op) INSTRUCTION(op, Mul, 2, lhs, traceSlot) #define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs) -#define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs) -#define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs) +#define INSTR_Mod(op) INSTRUCTION(op, Mod, 2, lhs, traceSlot) +#define INSTR_Sub(op) INSTRUCTION(op, Sub, 2, lhs, traceSlot) #define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result) #define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) #define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count) @@ -377,6 +377,12 @@ QT_BEGIN_NAMESPACE int arg2; \ int arg3; \ int arg4; +#define MOTH_DEFINE_ARGS5(arg1, arg2, arg3, arg4, arg5) \ + int arg1; \ + int arg2; \ + int arg3; \ + int arg4; \ + int arg5; #define MOTH_COLLECT_ENUMS(instr) \ INSTR_##instr(MOTH_GET_ENUM) @@ -447,6 +453,9 @@ QT_BEGIN_NAMESPACE #define MOTH_DECODE_ARGS4(name, type, nargs, arg1, arg2, arg3, arg4) \ MOTH_DECODE_ARGS3(name, type, nargs, arg1, arg2, arg3); \ MOTH_DECODE_ARG(arg4, type, nargs, 3); +#define MOTH_DECODE_ARGS5(name, type, nargs, arg1, arg2, arg3, arg4, arg5) \ + MOTH_DECODE_ARGS4(name, type, nargs, arg1, arg2, arg3, arg4); \ + MOTH_DECODE_ARG(arg5, type, nargs, 4); #ifdef MOTH_COMPUTED_GOTO /* collect jump labels */ diff --git a/src/qml/jit/qv4assemblercommon.cpp b/src/qml/jit/qv4assemblercommon.cpp index 35b8f34633..d5d97f8284 100644 --- a/src/qml/jit/qv4assemblercommon.cpp +++ b/src/qml/jit/qv4assemblercommon.cpp @@ -298,6 +298,19 @@ void PlatformAssemblerCommon::passInt32AsArg(int value, int arg) store32(TrustedImm32(value), argStackAddress(arg)); } +void JIT::PlatformAssemblerCommon::passPointerAsArg(void *ptr, int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; +#endif + + if (arg < ArgInRegCount) + move(TrustedImmPtr(ptr), registerForArg(arg)); + else + storePtr(TrustedImmPtr(ptr), argStackAddress(arg)); +} + void PlatformAssemblerCommon::callRuntime(const char *functionName, const void *funcPtr) { #ifndef QT_NO_DEBUG diff --git a/src/qml/jit/qv4assemblercommon_p.h b/src/qml/jit/qv4assemblercommon_p.h index c17fdd3a23..8f4d3238c4 100644 --- a/src/qml/jit/qv4assemblercommon_p.h +++ b/src/qml/jit/qv4assemblercommon_p.h @@ -587,7 +587,7 @@ public: Address loadCompilationUnitPtr(RegisterID target) { Address addr = loadFunctionPtr(target); - addr.offset = offsetof(QV4::Function, compilationUnit); + addr.offset = offsetof(QV4::FunctionData, compilationUnit); loadPtr(addr, target); return Address(target); } @@ -699,6 +699,7 @@ public: void passAddressAsArg(Address addr, int arg); void passCppFrameAsArg(int arg); void passInt32AsArg(int value, int arg); + void passPointerAsArg(void *ptr, int arg); void callRuntime(const char *functionName, const void *funcPtr); void callRuntimeUnchecked(const char *functionName, const void *funcPtr); void tailCallRuntime(const char *functionName, const void *funcPtr); diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp index 5c08c42977..9663754cbf 100644 --- a/src/qml/jit/qv4baselineassembler.cpp +++ b/src/qml/jit/qv4baselineassembler.cpp @@ -1458,6 +1458,11 @@ void BaselineAssembler::passInt32AsArg(int value, int arg) pasm()->passInt32AsArg(value, arg); } +void BaselineAssembler::passPointerAsArg(void *ptr, int arg) +{ + pasm()->passPointerAsArg(ptr, arg); +} + void BaselineAssembler::callRuntime(const char *functionName, const void *funcPtr, CallResultDestination dest) { pasm()->callRuntime(functionName, funcPtr, dest); diff --git a/src/qml/jit/qv4baselineassembler_p.h b/src/qml/jit/qv4baselineassembler_p.h index 0aa508ae71..dbefa42784 100644 --- a/src/qml/jit/qv4baselineassembler_p.h +++ b/src/qml/jit/qv4baselineassembler_p.h @@ -150,6 +150,7 @@ public: void passJSSlotAsArg(int reg, int arg); void passCppFrameAsArg(int arg); void passInt32AsArg(int value, int arg); + void passPointerAsArg(void *ptr, int arg); void callRuntime(const char *functionName, const void *funcPtr, CallResultDestination dest); void saveAccumulatorInFrame(); void jsTailCall(int func, int thisObject, int argc, int argv); diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index 098bbfc6c6..ed4cdabd26 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) +void BaselineJIT::generate_LoadLocal(int index, int /*traceSlot*/) { as->loadLocal(index); } @@ -160,7 +160,7 @@ void BaselineJIT::generate_StoreLocal(int index) as->storeLocal(index); } -void BaselineJIT::generate_LoadScopedLocal(int scope, int index) +void BaselineJIT::generate_LoadScopedLocal(int scope, int index, int /*traceSlot*/) { as->loadLocal(index, scope); } @@ -193,7 +193,7 @@ void BaselineJIT::generate_LoadClosure(int value) BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_closure, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_LoadName(int name) +void BaselineJIT::generate_LoadName(int name, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(2); @@ -203,7 +203,7 @@ void BaselineJIT::generate_LoadName(int name) as->checkException(); } -void BaselineJIT::generate_LoadGlobalLookup(int index) +void BaselineJIT::generate_LoadGlobalLookup(int index, int /*traceSlot*/) { as->prepareCallWithArgCount(3); as->passInt32AsArg(index, 2); @@ -237,7 +237,7 @@ void BaselineJIT::generate_StoreNameStrict(int name) as->checkException(); } -void BaselineJIT::generate_LoadElement(int base) +void BaselineJIT::generate_LoadElement(int base, int /*traceSlot*/) { STORE_IP(); STORE_ACC(); @@ -249,7 +249,7 @@ void BaselineJIT::generate_LoadElement(int base) as->checkException(); } -void BaselineJIT::generate_StoreElement(int base, int index) +void BaselineJIT::generate_StoreElement(int base, int index, int /*traceSlot*/) { STORE_IP(); STORE_ACC(); @@ -262,7 +262,7 @@ void BaselineJIT::generate_StoreElement(int base, int index) as->checkException(); } -void BaselineJIT::generate_LoadProperty(int name) +void BaselineJIT::generate_LoadProperty(int name, int /*traceSlot*/) { STORE_IP(); STORE_ACC(); @@ -274,7 +274,7 @@ void BaselineJIT::generate_LoadProperty(int name) as->checkException(); } -void BaselineJIT::generate_GetLookup(int index) +void BaselineJIT::generate_GetLookup(int index, int /*traceSlot*/) { STORE_IP(); STORE_ACC(); @@ -415,7 +415,7 @@ void BaselineJIT::generate_Resume(int) Q_UNREACHABLE(); } -void BaselineJIT::generate_CallValue(int name, int argc, int argv) +void BaselineJIT::generate_CallValue(int name, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -427,7 +427,7 @@ void BaselineJIT::generate_CallValue(int name, int argc, int argv) as->checkException(); } -void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv) +void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -440,7 +440,7 @@ void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, as->checkException(); } -void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv) +void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -453,7 +453,7 @@ void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv) as->checkException(); } -void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) +void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -466,7 +466,7 @@ void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int arg as->checkException(); } -void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv) +void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -479,7 +479,7 @@ void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv) as->checkException(); } -void BaselineJIT::generate_CallName(int name, int argc, int argv) +void BaselineJIT::generate_CallName(int name, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -491,7 +491,7 @@ void BaselineJIT::generate_CallName(int name, int argc, int argv) as->checkException(); } -void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv) +void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(3); @@ -502,7 +502,7 @@ void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv) as->checkException(); } -void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv) +void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -514,7 +514,7 @@ void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv) as->checkException(); } -void BaselineJIT::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv) +void BaselineJIT::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -527,7 +527,7 @@ void BaselineJIT::generate_CallScopeObjectProperty(int propIdx, int base, int ar as->checkException(); } -void BaselineJIT::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv) +void BaselineJIT::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -541,7 +541,7 @@ void BaselineJIT::generate_CallContextObjectProperty(int propIdx, int base, int } -void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv) +void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -871,8 +871,8 @@ void BaselineJIT::generate_ToObject() } void BaselineJIT::generate_Jump(int offset) { as->jump(absoluteOffsetForJump(offset)); } -void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(absoluteOffsetForJump(offset)); } -void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(absoluteOffsetForJump(offset)); } +void BaselineJIT::generate_JumpTrue(int /*traceSlot*/, int offset) { as->jumpTrue(absoluteOffsetForJump(offset)); } +void BaselineJIT::generate_JumpFalse(int /*traceSlot*/, int offset) { as->jumpFalse(absoluteOffsetForJump(offset)); } void BaselineJIT::generate_JumpNoException(int offset) { as->jumpNoException(absoluteOffsetForJump(offset)); } void BaselineJIT::generate_JumpNotUndefined(int offset) { as->jumpNotUndefined(absoluteOffsetForJump(offset)); } @@ -913,11 +913,11 @@ void BaselineJIT::generate_CmpInstanceOf(int lhs) void BaselineJIT::generate_UNot() { as->unot(); } void BaselineJIT::generate_UPlus() { as->toNumber(); } -void BaselineJIT::generate_UMinus() { as->uminus(); } +void BaselineJIT::generate_UMinus(int /*traceSlot*/) { as->uminus(); } void BaselineJIT::generate_UCompl() { as->ucompl(); } -void BaselineJIT::generate_Increment() { as->inc(); } -void BaselineJIT::generate_Decrement() { as->dec(); } -void BaselineJIT::generate_Add(int lhs) { as->add(lhs); } +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_BitAnd(int lhs) { as->bitAnd(lhs); } void BaselineJIT::generate_BitOr(int lhs) { as->bitOr(lhs); } @@ -942,10 +942,10 @@ void BaselineJIT::generate_Exp(int lhs) { BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::exp, CallResultDestination::InAccumulator); as->checkException(); } -void BaselineJIT::generate_Mul(int lhs) { as->mul(lhs); } +void BaselineJIT::generate_Mul(int lhs, int /*traceSlot*/) { as->mul(lhs); } void BaselineJIT::generate_Div(int lhs) { as->div(lhs); } -void BaselineJIT::generate_Mod(int lhs) { as->mod(lhs); } -void BaselineJIT::generate_Sub(int lhs) { as->sub(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_BinopContext(int alu, int lhs) //{ diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h index 98d23f4517..9e7dd8f2ca 100644 --- a/src/qml/jit/qv4baselinejit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -88,21 +88,21 @@ 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) override; + void generate_LoadLocal(int index, int traceSlot) override; void generate_StoreLocal(int index) override; - void generate_LoadScopedLocal(int scope, 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) override; - void generate_LoadGlobalLookup(int index) 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) override; - void generate_StoreElement(int base, int index) override; - void generate_LoadProperty(int name) override; - void generate_GetLookup(int index) 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; @@ -120,17 +120,17 @@ public: void generate_YieldStar() override; void generate_Resume(int) 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_CallScopeObjectProperty(int propIdx, int base, int argc, int argv) override; - void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv) override; - void generate_CallWithSpread(int func, int thisObject, int argc, int argv) 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_CallScopeObjectProperty(int propIdx, int base, int argc, int argv, int traceSlot) override; + void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv, int traceSlot) 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; @@ -169,8 +169,8 @@ public: void generate_LoadSuperConstructor() override; void generate_ToObject() override; void generate_Jump(int offset) override; - void generate_JumpTrue(int offset) override; - void generate_JumpFalse(int offset) override; + void generate_JumpTrue(int traceSlot, int offset) override; + void generate_JumpFalse(int traceSlot, int offset) override; void generate_JumpNoException(int offset) override; void generate_JumpNotUndefined(int offset) override; void generate_CmpEqNull() override; @@ -189,11 +189,11 @@ public: void generate_CmpInstanceOf(int lhs) override; void generate_UNot() override; void generate_UPlus() override; - void generate_UMinus() override; + void generate_UMinus(int traceSlot) override; void generate_UCompl() override; - void generate_Increment() override; - void generate_Decrement() override; - void generate_Add(int lhs) 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; @@ -207,10 +207,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) override; + void generate_Mul(int lhs, int traceSlot) override; void generate_Div(int lhs) override; - void generate_Mod(int lhs) override; - void generate_Sub(int lhs) override; + void generate_Mod(int lhs, int traceSlot) override; + void generate_Sub(int lhs, int traceSlot) override; void generate_LoadQmlContext(int result) override; void generate_LoadQmlImportedScripts(int result) override; void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index a482cc6a67..8adb84719f 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -50,6 +50,7 @@ #include <QDateTime> #include <QDir> #include <QFileInfo> +#include <QLoggingCategory> #ifndef V4_BOOTSTRAP @@ -134,6 +135,8 @@ QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcTracingAll, "qt.v4.tracing.all") + using namespace QV4; #ifndef V4_BOOTSTRAP @@ -646,6 +649,13 @@ 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 2a82d96f1d..7702939e23 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -73,13 +73,30 @@ ReturnedValue Function::call(const Value *thisObject, const Value *argv, int arg return result; } +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); +} + +void Function::destroy() +{ + delete[] reinterpret_cast<quint8 *>(this); +} + Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) - : compiledFunction(function) - , compilationUnit(unit) + : FunctionData(unit) + , compiledFunction(function) , codeData(function->code()) - , jittedCode(nullptr) - , codeRef(nullptr) - , hasQmlDependencies(function->hasQmlDependencies()) + , jittedCode(nullptr) + , codeRef(nullptr) + , hasQmlDependencies(function->hasQmlDependencies()) { Scope scope(engine); Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext)); @@ -95,6 +112,13 @@ 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() @@ -165,4 +189,22 @@ 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 86343ea061..374e46b929 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -64,10 +64,24 @@ struct QQmlSourceLocation; namespace QV4 { -struct Q_QML_EXPORT Function { - const CompiledData::Function *compiledFunction; +struct Q_QML_EXPORT FunctionData { CompiledData::CompilationUnit *compilationUnit; + FunctionData(CompiledData::CompilationUnit *compilationUnit) + : compilationUnit(compilationUnit) + {} +}; +// Make sure this class can be accessed through offsetof (done by the assemblers): +Q_STATIC_ASSERT(std::is_standard_layout< FunctionData >::value); + +struct Q_QML_EXPORT Function : public FunctionData { +private: + Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); + ~Function(); + +public: + const CompiledData::Function *compiledFunction; + ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context); const char *codeData; @@ -83,8 +97,8 @@ struct Q_QML_EXPORT Function { bool hasQmlDependencies; bool isEval = false; - Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); - ~Function(); + static Function *create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); + void destroy(); // used when dynamically assigning signal handlers (QQmlConnection) void updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters); @@ -110,6 +124,31 @@ struct Q_QML_EXPORT Function { 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 162fb66cba..44adac26cd 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -279,6 +279,20 @@ 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 90246c4229..a60a49a811 100644 --- a/src/qml/jsruntime/qv4math_p.h +++ b/src/qml/jsruntime/qv4math_p.h @@ -66,27 +66,42 @@ QT_BEGIN_NAMESPACE namespace QV4 { -static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b) +static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b, quint8 *traceInfo = nullptr) { int result; - if (Q_UNLIKELY(add_overflow(a, b, &result))) + if (Q_UNLIKELY(add_overflow(a, b, &result))) { + if (traceInfo) + *traceInfo |= quint8(QV4::ObservedTraceValues::Double); 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) +static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b, quint8 *traceInfo = nullptr) { int result; - if (Q_UNLIKELY(sub_overflow(a, b, &result))) + if (Q_UNLIKELY(sub_overflow(a, b, &result))) { + if (traceInfo) + *traceInfo |= quint8(QV4::ObservedTraceValues::Double); 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) +static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b, quint8 *traceInfo = nullptr) { int result; - if (Q_UNLIKELY(mul_overflow(a, b, &result))) + if (Q_UNLIKELY(mul_overflow(a, b, &result))) { + if (traceInfo) + *traceInfo |= quint8(QV4::ObservedTraceValues::Double); 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 13244fdd95..abf48b1034 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -695,6 +695,30 @@ ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value & return getElementFallback(engine, object, index); } +ReturnedValue Runtime::method_loadElement_traced(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); @@ -750,6 +774,30 @@ void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, engine->throwTypeError(); } +void Runtime::method_storeElement_traced(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::method_getIterator(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 06e8a8a960..dc4dbb11b1 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -112,9 +112,11 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { F(void, storeNameSloppy, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ F(void, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \ F(void, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \ + F(void, storeElement_traced, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value, quint8 *traceSlot)) \ F(ReturnedValue, loadProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \ F(ReturnedValue, loadName, (ExecutionEngine *engine, int nameIndex)) \ F(ReturnedValue, loadElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \ + F(ReturnedValue, loadElement_traced, (ExecutionEngine *engine, const Value &object, const Value &index, quint8 *traceSlot)) \ F(ReturnedValue, loadSuperProperty, (ExecutionEngine *engine, const Value &property)) \ F(void, storeSuperProperty, (ExecutionEngine *engine, const Value &property, const Value &value)) \ F(ReturnedValue, loadSuperConstructor, (ExecutionEngine *engine, const Value &t)) \ diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 937a535b83..d69a0fe34e 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -346,6 +346,70 @@ 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 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(); @@ -453,6 +517,11 @@ 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 (;;) { @@ -511,6 +580,7 @@ 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) @@ -523,6 +593,7 @@ 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) @@ -547,6 +618,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_IP(); acc = Runtime::method_loadName(engine, name); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) @@ -554,6 +626,7 @@ 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(StoreNameStrict) @@ -573,14 +646,25 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(LoadElement) STORE_IP(); STORE_ACC(); +#if QT_CONFIG(qml_tracing) + acc = Runtime::method_loadElement_traced(engine, STACK_VALUE(base), accumulator, function->traceInfo(traceSlot)); + traceValue(acc, function, traceSlot); +#else + Q_UNUSED(traceSlot); acc = Runtime::method_loadElement(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::method_storeElement_traced(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator, function->traceInfo(traceSlot)); +#else + Q_UNUSED(traceSlot); Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator); +#endif CHECK_EXCEPTION; MOTH_END_INSTR(StoreElement) @@ -589,6 +673,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_ACC(); acc = Runtime::method_loadProperty(engine, accumulator, name); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) @@ -597,6 +682,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; acc = l->getter(l, engine, accumulator); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) @@ -701,6 +787,7 @@ 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) @@ -712,12 +799,14 @@ 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::method_callProperty(engine, stack + base, name, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) @@ -733,48 +822,56 @@ 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::method_callElement(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::method_callName(engine, name, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallName) MOTH_BEGIN_INSTR(CallPossiblyDirectEval) STORE_IP(); acc = Runtime::method_callPossiblyDirectEval(engine, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) STORE_IP(); acc = Runtime::method_callGlobalLookup(engine, index, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(CallScopeObjectProperty) STORE_IP(); acc = Runtime::method_callQmlScopeObjectProperty(engine, stack + base, name, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallScopeObjectProperty) MOTH_BEGIN_INSTR(CallContextObjectProperty) STORE_IP(); acc = Runtime::method_callQmlContextObjectProperty(engine, stack + base, name, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallContextObjectProperty) MOTH_BEGIN_INSTR(CallWithSpread) STORE_IP(); acc = Runtime::method_callWithSpread(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallWithSpread) MOTH_BEGIN_INSTR(TailCall) @@ -1012,23 +1109,25 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(Jump) MOTH_BEGIN_INSTR(JumpTrue) - if (Q_LIKELY(ACC.integerCompatible())) { - if (ACC.int_32()) - code += offset; - } else { - if (ACC.toBoolean()) - code += offset; - } + bool takeJump; + if (Q_LIKELY(ACC.integerCompatible())) + takeJump = ACC.int_32(); + else + takeJump = ACC.toBoolean(); + traceJumpTakesTruePath(takeJump, function, traceSlot); + if (takeJump) + code += offset; MOTH_END_INSTR(JumpTrue) MOTH_BEGIN_INSTR(JumpFalse) - if (Q_LIKELY(ACC.integerCompatible())) { - if (!ACC.int_32()) - code += offset; - } else { - if (!ACC.toBoolean()) - code += offset; - } + bool takeJump; + if (Q_LIKELY(ACC.integerCompatible())) + takeJump = !ACC.int_32(); + else + takeJump = !ACC.toBoolean(); + traceJumpTakesTruePath(!takeJump, function, traceSlot); + if (takeJump) + code += offset; MOTH_END_INSTR(JumpFalse) MOTH_BEGIN_INSTR(JumpNoException) @@ -1197,14 +1296,17 @@ 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()); + acc = sub_int32(0, ACC.int_32(), function->traceInfo(traceSlot)); } } 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) @@ -1215,49 +1317,57 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(Increment) if (Q_LIKELY(ACC.integerCompatible())) { - acc = add_int32(ACC.int_32(), 1); + acc = add_int32(ACC.int_32(), 1, function->traceInfo(traceSlot)); } 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); + acc = sub_int32(ACC.int_32(), 1, function->traceInfo(traceSlot)); } 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()); + acc = add_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() + ACC.asDouble()); + traceDoubleValue(function, traceSlot); } else { STORE_ACC(); acc = Runtime::method_add(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()); + acc = sub_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() - ACC.asDouble()); + traceDoubleValue(function, traceSlot); } else { STORE_ACC(); acc = Runtime::method_sub(left, accumulator); CHECK_EXCEPTION; + traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Sub) @@ -1274,13 +1384,15 @@ 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()); + acc = mul_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() * ACC.asDouble()); + traceDoubleValue(function, traceSlot); } else { STORE_ACC(); acc = Runtime::method_mul(left, accumulator); CHECK_EXCEPTION; + traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Mul) @@ -1294,6 +1406,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_ACC(); acc = Runtime::method_mod(STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(Mod) MOTH_BEGIN_INSTR(BitAnd) diff --git a/src/qml/qtqmlglobal.h b/src/qml/qtqmlglobal.h index e02dfa5ed4..090b830b3c 100644 --- a/src/qml/qtqmlglobal.h +++ b/src/qml/qtqmlglobal.h @@ -53,6 +53,7 @@ #else # define QT_FEATURE_qml_debug -1 # define QT_FEATURE_qml_sequence_object 1 +# define QT_FEATURE_qml_tracing -1 #endif QT_BEGIN_NAMESPACE |