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/qml/compiler | |
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/qml/compiler')
-rw-r--r-- | src/qml/compiler/qv4bytecodegenerator_p.h | 60 | ||||
-rw-r--r-- | src/qml/compiler/qv4bytecodehandler.cpp | 6 | ||||
-rw-r--r-- | src/qml/compiler/qv4bytecodehandler_p.h | 6 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 84 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata.cpp | 5 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 7 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler.cpp | 2 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontext.cpp | 24 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontext_p.h | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth.cpp | 77 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 65 |
11 files changed, 223 insertions, 116 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 */ |