From a2372fd2c643615687ea3b8b1ccf53d699b6debd Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 2 Jul 2018 13:32:46 +0200 Subject: Allow for more than 128 bytecode instructions Instructions always occupy two numbers, an even and the following odd one, for the single byte and four byte encoding. An instruction of 0x0 is now a NOP, but 0x1 implies that the instruction type is using two bytes, and the following byte needs to be read as well to get the correct instruction type. Encoding and decoding of those two byte instructions is fully transparent, and adding more instructions now doesn't require any special handling. The first 127 instructions in the FOR_EACH_MOTH_INSTR macro will get a single byte encoding, the remaining ones will use two bytes. When adding new instructions, make sure to put often used and fast instructions into the first 127 instructions, while adding rarely used (or slow) instructions to the end (before Debug though). Change-Id: Id772a109641ab68feb228c3abd05f41ae7075e94 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4bytecodegenerator.cpp | 16 ++- src/qml/compiler/qv4instr_moth.cpp | 6 +- src/qml/compiler/qv4instr_moth_p.h | 163 ++++++++++++++++++------------ 3 files changed, 107 insertions(+), 78 deletions(-) (limited to 'src/qml/compiler') diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp index 6efeac2a31..7e1f49ee86 100644 --- a/src/qml/compiler/qv4bytecodegenerator.cpp +++ b/src/qml/compiler/qv4bytecodegenerator.cpp @@ -75,8 +75,9 @@ void BytecodeGenerator::packInstruction(I &i) type = Instr::narrowInstructionType(type); int instructionsAsInts[sizeof(Instr)/sizeof(int)] = {}; int nMembers = Moth::InstrInfo::argumentCount[static_cast(i.type)]; + uchar *code = i.packed + Instr::encodedLength(type); for (int j = 0; j < nMembers; ++j) { - instructionsAsInts[j] = qFromLittleEndian(i.packed + 1 + j * sizeof(int)); + instructionsAsInts[j] = qFromLittleEndian(code + j * sizeof(int)); } enum { Normal, @@ -88,11 +89,10 @@ void BytecodeGenerator::packInstruction(I &i) break; } } - uchar *code = i.packed; + code = i.packed; switch (width) { case Normal: - Instr::pack(code, type); - ++code; + code = Instr::pack(code, type); for (int n = 0; n < nMembers; ++n) { qint8 v = static_cast(instructionsAsInts[n]); memcpy(code, &v, 1); @@ -225,12 +225,10 @@ QT_WARNING_POP const int argCount = Moth::InstrInfo::argumentCount[static_cast(type)]; int s = argCount*sizeof(int); if (offsetOfOffset != -1) - offsetOfOffset += 1; - I instr{type, static_cast(s + 1), 0, currentLine, offsetOfOffset, -1, "\0\0" }; + offsetOfOffset += Instr::encodedLength(type); + I instr{type, static_cast(s + Instr::encodedLength(type)), 0, currentLine, offsetOfOffset, -1, "\0\0" }; uchar *code = instr.packed; - Instr::pack(code, Instr::wideInstructionType(type)); - ++code; - Q_ASSERT(static_cast(Instr::wideInstructionType(type)) < 256); + code = Instr::pack(code, Instr::wideInstructionType(type)); for (int j = 0; j < argCount; ++j) { qToLittleEndian(i.argumentsAsInts[j], code); diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index 6773f97c8b..8e474b3783 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -48,7 +48,7 @@ int InstrInfo::size(Instr::Type type) { #define MOTH_RETURN_INSTR_SIZE(I) case Instr::Type::I: case Instr::Type::I##_Wide: return InstrMeta::Size; switch (type) { - FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE) + FOR_EACH_MOTH_INSTR_ALL(MOTH_RETURN_INSTR_SIZE) } #undef MOTH_RETURN_INSTR_SIZE Q_UNREACHABLE(); @@ -111,6 +111,8 @@ static QString toString(QV4::ReturnedValue v) QDebug d = qDebug(); \ d.noquote(); \ d.nospace(); \ + if (static_cast(Instr::Type::instr) >= 0x100) \ + --base_ptr; \ d << alignedLineNumber(line) << alignedNumber(codeOffset).constData() << ": " \ << rawBytes(base_ptr, int(code - base_ptr)) << #instr << " "; @@ -123,7 +125,7 @@ namespace QV4 { namespace Moth { const int InstrInfo::argumentCount[] = { - FOR_EACH_MOTH_INSTR(MOTH_COLLECT_NARGS) + FOR_EACH_MOTH_INSTR_ALL(MOTH_COLLECT_NARGS) }; diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 29fc28c128..4e2dd370c4 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -62,6 +62,7 @@ QT_BEGIN_NAMESPACE op##_INSTRUCTION(name, nargs, __VA_ARGS__) /* for all jump instructions, the offset has to come last, to simplify the job of the bytecode generator */ +#define INSTR_Nop(op) INSTRUCTION(op, Nop, 0) #define INSTR_Ret(op) INSTRUCTION(op, Ret, 0) #define INSTR_Debug(op) INSTRUCTION(op, Debug, 0) #define INSTR_LoadConst(op) INSTRUCTION(op, LoadConst, 1, index) @@ -191,10 +192,12 @@ QT_BEGIN_NAMESPACE #define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result) #define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) +#define FOR_EACH_MOTH_INSTR_ALL(F) \ + F(Nop) \ + FOR_EACH_MOTH_INSTR(F) #define FOR_EACH_MOTH_INSTR(F) \ F(Ret) \ - F(Debug) \ F(LoadConst) \ F(LoadZero) \ F(LoadTrue) \ @@ -202,6 +205,7 @@ QT_BEGIN_NAMESPACE F(LoadNull) \ F(LoadUndefined) \ F(LoadInt) \ + F(LoadRuntimeString) \ F(MoveConst) \ F(LoadReg) \ F(StoreReg) \ @@ -210,7 +214,6 @@ QT_BEGIN_NAMESPACE F(StoreLocal) \ F(LoadScopedLocal) \ F(StoreScopedLocal) \ - F(LoadRuntimeString) \ F(MoveRegExp) \ F(LoadClosure) \ F(LoadName) \ @@ -230,51 +233,7 @@ QT_BEGIN_NAMESPACE F(LoadScopeObjectProperty) \ F(LoadContextObjectProperty) \ F(LoadIdObject) \ - F(Yield) \ - F(Resume) \ - F(CallValue) \ - F(CallProperty) \ - F(CallPropertyLookup) \ - F(CallElement) \ - F(CallName) \ - F(CallPossiblyDirectEval) \ - F(CallGlobalLookup) \ - F(CallScopeObjectProperty) \ - F(CallContextObjectProperty) \ - F(CallWithSpread) \ - F(Construct) \ - F(ConstructWithSpread) \ - F(SetUnwindHandler) \ - F(UnwindDispatch) \ - F(UnwindToLabel) \ - F(ThrowException) \ - F(GetException) \ - F(SetException) \ - F(CreateCallContext) \ - F(PushCatchContext) \ - F(PushWithContext) \ - F(PushBlockContext) \ - F(CloneBlockContext) \ - F(PushScriptContext) \ - F(PopScriptContext) \ - F(PopContext) \ - F(GetIterator) \ - F(IteratorNext) \ - F(IteratorClose) \ - F(DestructureRestElement) \ - F(DeleteProperty) \ - F(DeleteName) \ - F(TypeofName) \ - F(TypeofValue) \ - F(DeclareVar) \ - F(DefineArray) \ - F(DefineObjectLiteral) \ - F(CreateClass) \ - F(CreateMappedArgumentsObject) \ - F(CreateUnmappedArgumentsObject) \ - F(CreateRestParameter) \ F(ConvertThisToObject) \ - F(LoadSuperConstructor) \ F(ToObject) \ F(Jump) \ F(JumpTrue) \ @@ -319,9 +278,55 @@ QT_BEGIN_NAMESPACE F(Div) \ F(Mod) \ F(Sub) \ + F(CallValue) \ + F(CallProperty) \ + F(CallPropertyLookup) \ + F(CallElement) \ + F(CallName) \ + F(CallPossiblyDirectEval) \ + F(CallGlobalLookup) \ + F(CallScopeObjectProperty) \ + F(CallContextObjectProperty) \ + F(CallWithSpread) \ + F(Construct) \ + F(ConstructWithSpread) \ + F(SetUnwindHandler) \ + F(UnwindDispatch) \ + F(UnwindToLabel) \ + F(ThrowException) \ + F(GetException) \ + F(SetException) \ + F(CreateCallContext) \ + F(PushCatchContext) \ + F(PushWithContext) \ + F(PushBlockContext) \ + F(CloneBlockContext) \ + F(PopContext) \ + F(GetIterator) \ + F(IteratorNext) \ + F(IteratorClose) \ + F(DestructureRestElement) \ + F(DeleteProperty) \ + F(DeleteName) \ + F(TypeofName) \ + F(TypeofValue) \ + F(DeclareVar) \ + F(DefineArray) \ + F(DefineObjectLiteral) \ + F(CreateMappedArgumentsObject) \ + F(CreateUnmappedArgumentsObject) \ + F(CreateRestParameter) \ F(LoadQmlContext) \ - F(LoadQmlImportedScripts) -#define MOTH_NUM_INSTRUCTIONS() (static_cast(Moth::Instr::Type::LoadQmlImportedScripts_Wide)) + F(LoadQmlImportedScripts) \ + F(Yield) \ + F(Resume) \ + F(CreateClass) \ + F(LoadSuperConstructor) \ + F(PushScriptContext) \ + F(PopScriptContext) \ + F(Debug) \ + +#define MOTH_NUM_INSTRUCTIONS() (static_cast(Moth::Instr::Type::Debug_Wide) + 1) #if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) // icc before version 1200 doesn't support computed goto, and at least up to version 18.0.0 the @@ -439,27 +444,44 @@ QT_BEGIN_NAMESPACE #define MOTH_JUMP_TABLE \ static const void *jumpTable[] = { \ - FOR_EACH_MOTH_INSTR(COLLECT_LABELS) \ + FOR_EACH_MOTH_INSTR_ALL(COLLECT_LABELS) \ }; -#define MOTH_DISPATCH() \ +#define MOTH_DISPATCH_SINGLE() \ goto *jumpTable[*reinterpret_cast(code)]; + +#define MOTH_DISPATCH() \ + MOTH_DISPATCH_SINGLE() \ + op_byte_Nop: \ + ++code; \ + MOTH_DISPATCH_SINGLE() \ + op_int_Nop: /* wide prefix */ \ + ++code; \ + goto *jumpTable[0x100 | *reinterpret_cast(code)]; #else #define MOTH_JUMP_TABLE #define MOTH_INSTR_CASE_AND_JUMP(instr) \ - INSTR_##instr(GET_CASE_AND_JUMP) -#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \ - case static_cast(Instr::Type::name): goto op_byte_##name; -#define MOTH_INSTR_CASE_AND_JUMP_WIDE(instr) \ + INSTR_##instr(GET_CASE_AND_JUMP) \ INSTR_##instr(GET_CASE_AND_JUMP_WIDE) +#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \ + case Instr::Type::name: goto op_byte_##name; #define GET_CASE_AND_JUMP_WIDE_INSTRUCTION(name, ...) \ - case (static_cast(Instr::Type::name##_Wide)): goto op_int_##name; + case Instr::Type::name##_Wide: goto op_int_##name; #define MOTH_DISPATCH() \ - switch (static_cast(*code)) { \ + Instr::Type type = Instr::Type(static_cast(*code)); \ + dispatch: \ + switch (type) { \ + case Instr::Type::Nop: \ + ++code; \ + type = Instr::Type(static_cast(*code)); \ + goto dispatch; \ + case Instr::Type::Nop_Wide: /* wide prefix */ \ + ++code; \ + type = Instr::Type(0x100 | static_cast(*code)); \ + goto dispatch; \ FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP) \ - FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP_WIDE) \ } #endif @@ -502,20 +524,29 @@ inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals, union Instr { enum class Type { - FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) + FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_ENUM) }; static Type wideInstructionType(Type t) { return Type(int(t) | 1); } static Type narrowInstructionType(Type t) { return Type(int(t) & ~1); } static bool isWide(Type t) { return int(t) & 1; } static bool isNarrow(Type t) { return !(int(t) & 1); } + static int encodedLength(Type t) { return int(t) >= 256 ? 2 : 1; } + + static Type unpack(const uchar *c) { if (c[0] == 0x1) return Type(0x100 + c[1]); return Type(c[0]); } + static uchar *pack(uchar *c, Type t) { + if (uint(t) >= 256) { + c[0] = 0x1; + c[1] = uint(t) &0xff; + return c + 2; + } + c[0] = uchar(uint(t)); + return c + 1; + } - static Type unpack(const uchar *c) { return Type(*c); } - static void pack(uchar *c, Type t) { *c = uchar(uint(t)); } - - FOR_EACH_MOTH_INSTR(MOTH_EMIT_STRUCTS) + FOR_EACH_MOTH_INSTR_ALL(MOTH_EMIT_STRUCTS) - FOR_EACH_MOTH_INSTR(MOTH_EMIT_INSTR_MEMBERS) + FOR_EACH_MOTH_INSTR_ALL(MOTH_EMIT_INSTR_MEMBERS) int argumentsAsInts[4]; }; @@ -526,8 +557,6 @@ struct InstrInfo static int size(Instr::Type type); }; -Q_STATIC_ASSERT(MOTH_NUM_INSTRUCTIONS() <= 256); - template struct InstrMeta { }; @@ -545,7 +574,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") reinterpret_cast(&v), \ Size); } \ }; -FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); +FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_META_TEMPLATE); #undef MOTH_INSTR_META_TEMPLATE QT_WARNING_POP @@ -556,7 +585,7 @@ class InstrData : public InstrMeta::DataType struct Instruction { #define MOTH_INSTR_DATA_TYPEDEF(I) typedef InstrData I; -FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) +FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_DATA_TYPEDEF) #undef MOTH_INSTR_DATA_TYPEDEF private: Instruction(); -- cgit v1.2.3