aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jit
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jit')
-rw-r--r--src/qml/jit/jit.pri33
-rw-r--r--src/qml/jit/qv4baselineassembler.cpp60
-rw-r--r--src/qml/jit/qv4baselineassembler_p.h2
-rw-r--r--src/qml/jit/qv4baselinejit.cpp232
-rw-r--r--src/qml/jit/qv4baselinejit_p.h4
-rw-r--r--src/qml/jit/qv4blockscheduler.cpp208
-rw-r--r--src/qml/jit/qv4blockscheduler_p.h155
-rw-r--r--src/qml/jit/qv4domtree.cpp436
-rw-r--r--src/qml/jit/qv4domtree_p.h136
-rw-r--r--src/qml/jit/qv4graph.cpp103
-rw-r--r--src/qml/jit/qv4graph_p.h146
-rw-r--r--src/qml/jit/qv4graphbuilder.cpp1683
-rw-r--r--src/qml/jit/qv4graphbuilder_p.h298
-rw-r--r--src/qml/jit/qv4ir.cpp382
-rw-r--r--src/qml/jit/qv4ir_p.h228
-rw-r--r--src/qml/jit/qv4jithelpers.cpp174
-rw-r--r--src/qml/jit/qv4loopinfo.cpp199
-rw-r--r--src/qml/jit/qv4loopinfo_p.h159
-rw-r--r--src/qml/jit/qv4lowering.cpp200
-rw-r--r--src/qml/jit/qv4lowering_p.h (renamed from src/qml/jit/qv4jithelpers_p.h)67
-rw-r--r--src/qml/jit/qv4mi.cpp251
-rw-r--r--src/qml/jit/qv4mi_p.h627
-rw-r--r--src/qml/jit/qv4miblockset_p.h291
-rw-r--r--src/qml/jit/qv4node.cpp215
-rw-r--r--src/qml/jit/qv4node_p.h626
-rw-r--r--src/qml/jit/qv4operation.cpp770
-rw-r--r--src/qml/jit/qv4operation_p.h567
-rw-r--r--src/qml/jit/qv4runtimesupport_p.h255
-rw-r--r--src/qml/jit/qv4schedulers.cpp912
-rw-r--r--src/qml/jit/qv4schedulers_p.h162
-rw-r--r--src/qml/jit/qv4tracingjit.cpp91
31 files changed, 9299 insertions, 373 deletions
diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri
index 2c664af188..bc0d20dba7 100644
--- a/src/qml/jit/jit.pri
+++ b/src/qml/jit/jit.pri
@@ -2,13 +2,42 @@ INCLUDEPATH += $$PWD
INCLUDEPATH += $$OUT_PWD
SOURCES += \
- $$PWD/qv4jithelpers.cpp \
$$PWD/qv4baselinejit.cpp \
$$PWD/qv4baselineassembler.cpp \
$$PWD/qv4assemblercommon.cpp
HEADERS += \
- $$PWD/qv4jithelpers_p.h \
$$PWD/qv4baselinejit_p.h \
$$PWD/qv4baselineassembler_p.h \
$$PWD/qv4assemblercommon_p.h
+
+qtConfig(qml-tracing) {
+SOURCES += \
+ $$PWD/qv4ir.cpp \
+ $$PWD/qv4operation.cpp \
+ $$PWD/qv4node.cpp \
+ $$PWD/qv4graph.cpp \
+ $$PWD/qv4graphbuilder.cpp \
+ $$PWD/qv4lowering.cpp \
+ $$PWD/qv4tracingjit.cpp \
+ $$PWD/qv4mi.cpp \
+ $$PWD/qv4domtree.cpp \
+ $$PWD/qv4schedulers.cpp \
+ $$PWD/qv4blockscheduler.cpp \
+ $$PWD/qv4loopinfo.cpp
+
+HEADERS += \
+ $$PWD/qv4ir_p.h \
+ $$PWD/qv4operation_p.h \
+ $$PWD/qv4runtimesupport_p.h \
+ $$PWD/qv4node_p.h \
+ $$PWD/qv4graph_p.h \
+ $$PWD/qv4graphbuilder_p.h \
+ $$PWD/qv4lowering_p.h \
+ $$PWD/qv4mi_p.h \
+ $$PWD/qv4miblockset_p.h \
+ $$PWD/qv4domtree_p.h \
+ $$PWD/qv4schedulers_p.h \
+ $$PWD/qv4blockscheduler_p.h \
+ $$PWD/qv4loopinfo_p.h
+}
diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp
index 25c74e74e8..238c11f478 100644
--- a/src/qml/jit/qv4baselineassembler.cpp
+++ b/src/qml/jit/qv4baselineassembler.cpp
@@ -943,7 +943,7 @@ void BaselineAssembler::uminus()
saveAccumulatorInFrame();
pasm()->prepareCallWithArgCount(1);
pasm()->passAccumulatorAsArg(0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_uMinus, CallResultDestination::InAccumulator);
+ ASM_GENERATE_RUNTIME_CALL(UMinus, CallResultDestination::InAccumulator);
checkException();
}
@@ -1044,7 +1044,7 @@ void BaselineAssembler::add(int lhs)
pasm()->passAccumulatorAsArg(2);
pasm()->passJSSlotAsArg(lhs, 1);
pasm()->passEngineAsArg(0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_add, CallResultDestination::InAccumulator);
+ ASM_GENERATE_RUNTIME_CALL(Add, CallResultDestination::InAccumulator);
checkException();
// done.
@@ -1196,7 +1196,7 @@ void BaselineAssembler::mul(int lhs)
pasm()->prepareCallWithArgCount(2);
pasm()->passAccumulatorAsArg(1);
pasm()->passJSSlotAsArg(lhs, 0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_mul, CallResultDestination::InAccumulator);
+ ASM_GENERATE_RUNTIME_CALL(Mul, CallResultDestination::InAccumulator);
checkException();
// done.
@@ -1209,7 +1209,7 @@ void BaselineAssembler::div(int lhs)
pasm()->prepareCallWithArgCount(2);
pasm()->passAccumulatorAsArg(1);
pasm()->passJSSlotAsArg(lhs, 0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_div, CallResultDestination::InAccumulator);
+ ASM_GENERATE_RUNTIME_CALL(Div, CallResultDestination::InAccumulator);
checkException();
}
@@ -1219,7 +1219,7 @@ void BaselineAssembler::mod(int lhs)
pasm()->prepareCallWithArgCount(2);
pasm()->passAccumulatorAsArg(1);
pasm()->passJSSlotAsArg(lhs, 0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_mod, CallResultDestination::InAccumulator);
+ ASM_GENERATE_RUNTIME_CALL(Mod, CallResultDestination::InAccumulator);
checkException();
}
@@ -1239,7 +1239,7 @@ void BaselineAssembler::sub(int lhs)
pasm()->prepareCallWithArgCount(2);
pasm()->passAccumulatorAsArg(1);
pasm()->passJSSlotAsArg(lhs, 0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_sub, CallResultDestination::InAccumulator);
+ ASM_GENERATE_RUNTIME_CALL(Sub, CallResultDestination::InAccumulator);
checkException();
// done.
@@ -1269,7 +1269,7 @@ void BaselineAssembler::cmpeqInt(int lhs)
else
pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
pasm()->pushAccumulatorAsArg(0);
- pasm()->callRuntimeUnchecked("Runtime::method_equal", (void*)Runtime::method_equal);
+ pasm()->callRuntimeUnchecked("Equal", (void*)Runtime::Equal::call);
pasm()->saveReturnValueInAccumulator();
if (PlatformAssembler::ArgInRegCount < 2)
pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
@@ -1293,7 +1293,7 @@ void BaselineAssembler::cmpneInt(int lhs)
else
pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
pasm()->pushAccumulatorAsArg(0);
- pasm()->callRuntimeUnchecked("Runtime::method_notEqual", (void*)Runtime::method_notEqual);
+ pasm()->callRuntimeUnchecked("NotEqual", (void*)Runtime::NotEqual::call);
pasm()->saveReturnValueInAccumulator();
if (PlatformAssembler::ArgInRegCount < 2)
pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
@@ -1314,7 +1314,6 @@ void BaselineAssembler::cmp(int cond, CmpFunc function, const char *functionName
pasm()->compare32(c, PlatformAssembler::ScratchRegister,
PlatformAssembler::AccumulatorRegisterValue,
PlatformAssembler::AccumulatorRegisterValue);
- pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
return PlatformAssembler::Jump();
});
@@ -1326,60 +1325,58 @@ void BaselineAssembler::cmp(int cond, CmpFunc function, const char *functionName
callRuntime(functionName, reinterpret_cast<void*>(function), CallResultDestination::InAccumulator);
checkException();
- pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
// done.
done.link(pasm());
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
}
void BaselineAssembler::cmpeq(int lhs)
{
- cmp(PlatformAssembler::Equal, &Runtime::method_compareEqual,
- "Runtime::method_compareEqual", lhs);
+ cmp(PlatformAssembler::Equal, &Runtime::CompareEqual::call,
+ "CompareEqual", lhs);
}
void BaselineAssembler::cmpne(int lhs)
{
- cmp(PlatformAssembler::NotEqual, &Runtime::method_compareNotEqual,
- "Runtime::method_compareNotEqual", lhs);
+ cmp(PlatformAssembler::NotEqual, &Runtime::CompareNotEqual::call,
+ "CompareNotEqual", lhs);
}
void BaselineAssembler::cmpgt(int lhs)
{
- cmp(PlatformAssembler::GreaterThan, &Runtime::method_compareGreaterThan,
- "Runtime::method_compareGreaterThan", lhs);
+ cmp(PlatformAssembler::GreaterThan, &Runtime::CompareGreaterThan::call,
+ "CompareGreaterThan", lhs);
}
void BaselineAssembler::cmpge(int lhs)
{
- cmp(PlatformAssembler::GreaterThanOrEqual, &Runtime::method_compareGreaterEqual,
- "Runtime::method_compareGreaterEqual", lhs);
+ cmp(PlatformAssembler::GreaterThanOrEqual, &Runtime::CompareGreaterEqual::call,
+ "CompareGreaterEqual", lhs);
}
void BaselineAssembler::cmplt(int lhs)
{
- cmp(PlatformAssembler::LessThan, &Runtime::method_compareLessThan,
- "Runtime::method_compareLessThan", lhs);
+ cmp(PlatformAssembler::LessThan, &Runtime::CompareLessThan::call,
+ "CompareLessThan", lhs);
}
void BaselineAssembler::cmple(int lhs)
{
- cmp(PlatformAssembler::LessThanOrEqual, &Runtime::method_compareLessEqual,
- "Runtime::method_compareLessEqual", lhs);
+ cmp(PlatformAssembler::LessThanOrEqual, &Runtime::CompareLessEqual::call,
+ "CompareLessEqual", lhs);
}
void BaselineAssembler::cmpStrictEqual(int lhs)
{
- cmp(PlatformAssembler::Equal, &RuntimeHelpers::strictEqual,
+ cmp(PlatformAssembler::Equal, &Runtime::CompareStrictEqual::call,
"RuntimeHelpers::strictEqual", lhs);
}
void BaselineAssembler::cmpStrictNotEqual(int lhs)
{
- cmp(PlatformAssembler::Equal, &RuntimeHelpers::strictEqual,
- "RuntimeHelpers::strictEqual", lhs);
- pasm()->xor32(TrustedImm32(1), PlatformAssembler::AccumulatorRegisterValue);
- pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+ cmp(PlatformAssembler::NotEqual, &Runtime::CompareStrictNotEqual::call,
+ "RuntimeHelpers::strictNotEqual", lhs);
}
int BaselineAssembler::jump(int offset)
@@ -1481,7 +1478,7 @@ void BaselineAssembler::saveAccumulatorInFrame()
static ReturnedValue TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing(CppStackFrame *frame, ExecutionEngine *engine)
{
- return Runtime::method_tailCall(frame, engine);
+ return Runtime::TailCall::call(frame, engine);
}
void BaselineAssembler::jsTailCall(int func, int thisObject, int argc, int argv)
@@ -1588,9 +1585,8 @@ void BaselineAssembler::pushCatchContext(int index, int name)
pasm()->prepareCallWithArgCount(3);
pasm()->passInt32AsArg(name, 2);
pasm()->passInt32AsArg(index, 1);
- pasm()->passJSSlotAsArg(CallData::Context, 0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_createCatchContext, CallResultDestination::InAccumulator);
- pasm()->storeAccumulator(pasm()->contextAddress());
+ pasm()->passEngineAsArg(0);
+ ASM_GENERATE_RUNTIME_CALL(PushCatchContext, CallResultDestination::Ignore);
}
void BaselineAssembler::popContext()
@@ -1610,7 +1606,7 @@ void BaselineAssembler::deadTemporalZoneCheck(int offsetForSavedIP, int variable
prepareCallWithArgCount(2);
passInt32AsArg(variableName, 1);
passEngineAsArg(0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_throwReferenceError, CallResultDestination::Ignore);
+ ASM_GENERATE_RUNTIME_CALL(ThrowReferenceError, CallResultDestination::Ignore);
gotoCatchException();
valueIsAliveJump.link(pasm());
}
diff --git a/src/qml/jit/qv4baselineassembler_p.h b/src/qml/jit/qv4baselineassembler_p.h
index c39d002bf9..3bbaefd000 100644
--- a/src/qml/jit/qv4baselineassembler_p.h
+++ b/src/qml/jit/qv4baselineassembler_p.h
@@ -65,7 +65,7 @@ namespace JIT {
#define GENERATE_RUNTIME_CALL(function, destination) \
callRuntime(JIT_STRINGIFY(function), \
- reinterpret_cast<void *>(&function), \
+ reinterpret_cast<void *>(&Runtime::function::call), \
destination)
#define GENERATE_TAIL_CALL(function) \
tailCallRuntime(JIT_STRINGIFY(function), \
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
index e518fc5a0e..80155d7b20 100644
--- a/src/qml/jit/qv4baselinejit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qv4baselinejit_p.h"
-#include "qv4jithelpers_p.h"
#include "qv4baselineassembler_p.h"
#include <private/qv4lookup_p.h>
#include <private/qv4generatorobject_p.h>
@@ -77,10 +76,11 @@ void BaselineJIT::generate()
#define STORE_IP() as->storeInstructionPointer(nextInstructionOffset())
#define STORE_ACC() as->saveAccumulatorInFrame()
-#define BASELINEJIT_GENERATE_RUNTIME_CALL(function, destination) \
- as->GENERATE_RUNTIME_CALL(function, destination)
-#define BASELINEJIT_GENERATE_TAIL_CALL(function) \
- as->GENERATE_TAIL_CALL(function)
+#define BASELINEJIT_GENERATE_RUNTIME_CALL(function, destination) { \
+ as->GENERATE_RUNTIME_CALL(function, destination); \
+ if (Runtime::function::throws) \
+ as->checkException(); \
+ else {} } // this else prevents else statements after the macro from attaching to the if above
void BaselineJIT::generate_Ret()
{
@@ -183,7 +183,7 @@ void BaselineJIT::generate_MoveRegExp(int regExpId, int destReg)
as->prepareCallWithArgCount(2);
as->passInt32AsArg(regExpId, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_regexpLiteral, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(RegexpLiteral, CallResultDestination::InAccumulator);
as->storeReg(destReg);
}
@@ -192,7 +192,7 @@ void BaselineJIT::generate_LoadClosure(int value)
as->prepareCallWithArgCount(2);
as->passInt32AsArg(value, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_closure, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Closure, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_LoadName(int name, int /*traceSlot*/)
@@ -201,28 +201,24 @@ void BaselineJIT::generate_LoadName(int name, int /*traceSlot*/)
as->prepareCallWithArgCount(2);
as->passInt32AsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadName, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadName, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_LoadGlobalLookup(int index, int /*traceSlot*/)
{
as->prepareCallWithArgCount(3);
as->passInt32AsArg(index, 2);
- as->passEngineAsArg(1);
- as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::loadGlobalLookup, CallResultDestination::InAccumulator);
- as->checkException();
+ as->passFunctionAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadGlobalLookup, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index, int /*traceSlot*/)
{
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(index, 2);
- as->passEngineAsArg(1);
- as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::loadQmlContextPropertyLookup, CallResultDestination::InAccumulator);
- as->checkException();
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(index, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContextPropertyLookup, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_StoreNameSloppy(int name)
@@ -233,8 +229,7 @@ void BaselineJIT::generate_StoreNameSloppy(int name)
as->passAccumulatorAsArg(2);
as->passInt32AsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameSloppy, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(StoreNameSloppy, CallResultDestination::Ignore);
}
void BaselineJIT::generate_StoreNameStrict(int name)
@@ -245,8 +240,7 @@ void BaselineJIT::generate_StoreNameStrict(int name)
as->passAccumulatorAsArg(2);
as->passInt32AsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameStrict, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(StoreNameStrict, CallResultDestination::Ignore);
}
void BaselineJIT::generate_LoadElement(int base, int /*traceSlot*/)
@@ -257,8 +251,7 @@ void BaselineJIT::generate_LoadElement(int base, int /*traceSlot*/)
as->passAccumulatorAsArg(2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadElement, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_StoreElement(int base, int index, int /*traceSlot*/)
@@ -270,8 +263,7 @@ void BaselineJIT::generate_StoreElement(int base, int index, int /*traceSlot*/)
as->passJSSlotAsArg(index, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeElement, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(StoreElement, CallResultDestination::Ignore);
}
void BaselineJIT::generate_LoadProperty(int name, int /*traceSlot*/)
@@ -282,8 +274,7 @@ void BaselineJIT::generate_LoadProperty(int name, int /*traceSlot*/)
as->passInt32AsArg(name, 2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadProperty, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_GetLookup(int index, int /*traceSlot*/)
@@ -293,10 +284,9 @@ void BaselineJIT::generate_GetLookup(int index, int /*traceSlot*/)
as->prepareCallWithArgCount(4);
as->passInt32AsArg(index, 3);
as->passAccumulatorAsArg(2);
- as->passEngineAsArg(1);
- as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::getLookup, CallResultDestination::InAccumulator);
- as->checkException();
+ as->passFunctionAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(GetLookup, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_StoreProperty(int name, int base)
@@ -308,8 +298,7 @@ void BaselineJIT::generate_StoreProperty(int name, int base)
as->passInt32AsArg(name, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeProperty, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(StoreProperty, CallResultDestination::Ignore);
}
void BaselineJIT::generate_SetLookup(int index, int base)
@@ -318,12 +307,13 @@ void BaselineJIT::generate_SetLookup(int index, int base)
STORE_ACC();
as->prepareCallWithArgCount(4);
as->passAccumulatorAsArg(3);
- as->passJSSlotAsArg(base, 2);
- as->passInt32AsArg(index, 1);
+ as->passInt32AsArg(index, 2);
+ as->passJSSlotAsArg(base, 1);
as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL((function->isStrict() ? Helpers::setLookupStrict : Helpers::setLookupSloppy),
- CallResultDestination::InAccumulator);
- as->checkException();
+ if (function->isStrict())
+ BASELINEJIT_GENERATE_RUNTIME_CALL(SetLookupStrict, CallResultDestination::InAccumulator)
+ else
+ BASELINEJIT_GENERATE_RUNTIME_CALL(SetLookupSloppy, CallResultDestination::InAccumulator)
}
void BaselineJIT::generate_LoadSuperProperty(int property)
@@ -333,8 +323,7 @@ void BaselineJIT::generate_LoadSuperProperty(int property)
as->prepareCallWithArgCount(2);
as->passJSSlotAsArg(property, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadSuperProperty, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadSuperProperty, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_StoreSuperProperty(int property)
@@ -345,8 +334,7 @@ void BaselineJIT::generate_StoreSuperProperty(int property)
as->passAccumulatorAsArg(2);
as->passJSSlotAsArg(property, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeSuperProperty, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(StoreSuperProperty, CallResultDestination::Ignore);
}
void BaselineJIT::generate_Yield()
@@ -375,8 +363,7 @@ void BaselineJIT::generate_CallValue(int name, int argc, int argv, int /*traceSl
as->passJSSlotAsArg(argv, 2);
as->passJSSlotAsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callValue, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallValue, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv, int /*traceSlot*/)
@@ -388,8 +375,7 @@ void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc,
as->passJSSlotAsArg(thisObject, 2);
as->passJSSlotAsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callWithReceiver, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallWithReceiver, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv, int /*traceSlot*/)
@@ -401,8 +387,7 @@ void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv,
as->passInt32AsArg(name, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callProperty, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallProperty, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, int /*traceSlot*/)
@@ -414,8 +399,7 @@ void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int arg
as->passInt32AsArg(lookupIndex, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callPropertyLookup, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallPropertyLookup, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv, int /*traceSlot*/)
@@ -427,8 +411,7 @@ void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv,
as->passJSSlotAsArg(index, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callElement, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallElement, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CallName(int name, int argc, int argv, int /*traceSlot*/)
@@ -439,8 +422,7 @@ void BaselineJIT::generate_CallName(int name, int argc, int argv, int /*traceSlo
as->passJSSlotAsArg(argv, 2);
as->passInt32AsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callName, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallName, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv, int /*traceSlot*/)
@@ -450,8 +432,7 @@ void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv, int /*trac
as->passInt32AsArg(argc, 2);
as->passJSSlotAsArg(argv, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callPossiblyDirectEval, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallPossiblyDirectEval, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int /*traceSlot*/)
@@ -462,8 +443,7 @@ void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int /
as->passJSSlotAsArg(argv, 2);
as->passInt32AsArg(index, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callGlobalLookup, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallGlobalLookup, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv,
@@ -475,8 +455,7 @@ void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int
as->passJSSlotAsArg(argv, 2);
as->passInt32AsArg(index, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callQmlContextPropertyLookup, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlContextPropertyLookup, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv, int /*traceSlot*/)
@@ -488,8 +467,7 @@ void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, in
as->passJSSlotAsArg(thisObject, 2);
as->passJSSlotAsArg(func, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callWithSpread, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallWithSpread, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_TailCall(int func, int thisObject, int argc, int argv)
@@ -508,8 +486,7 @@ void BaselineJIT::generate_Construct(int func, int argc, int argv)
as->passAccumulatorAsArg(2);
as->passJSSlotAsArg(func, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_construct, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Construct, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_ConstructWithSpread(int func, int argc, int argv)
@@ -522,8 +499,7 @@ void BaselineJIT::generate_ConstructWithSpread(int func, int argc, int argv)
as->passAccumulatorAsArg(2);
as->passJSSlotAsArg(func, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_constructWithSpread, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ConstructWithSpread, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_SetUnwindHandler(int offset)
@@ -556,7 +532,7 @@ void BaselineJIT::generate_ThrowException()
as->prepareCallWithArgCount(2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_throwException, CallResultDestination::Ignore);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ThrowException, CallResultDestination::Ignore);
as->gotoCatchException();
}
@@ -567,8 +543,7 @@ void BaselineJIT::generate_CreateCallContext()
{
as->prepareCallWithArgCount(1);
as->passCppFrameAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(ExecutionContext::newCallContext, CallResultDestination::Ignore); // keeps result in return value register
- as->storeHeapObject(CallData::Context);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(PushCallContext, CallResultDestination::Ignore);
}
void BaselineJIT::generate_PushCatchContext(int index, int name) { as->pushCatchContext(index, name); }
@@ -578,11 +553,9 @@ void BaselineJIT::generate_PushWithContext()
STORE_IP();
as->saveAccumulatorInFrame();
as->prepareCallWithArgCount(2);
- as->passJSSlotAsArg(0, 1);
+ as->passJSSlotAsArg(CallData::Accumulator, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createWithContext, CallResultDestination::Ignore); // keeps result in return value register
- as->checkException();
- as->storeHeapObject(CallData::Context);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(PushWithContext, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_PushBlockContext(int index)
@@ -590,35 +563,33 @@ void BaselineJIT::generate_PushBlockContext(int index)
as->saveAccumulatorInFrame();
as->prepareCallWithArgCount(2);
as->passInt32AsArg(index, 1);
- as->passJSSlotAsArg(0, 0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::pushBlockContext, CallResultDestination::Ignore);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(PushBlockContext, CallResultDestination::Ignore);
}
void BaselineJIT::generate_CloneBlockContext()
{
as->saveAccumulatorInFrame();
as->prepareCallWithArgCount(1);
- as->passJSSlotAsArg(CallData::Context, 0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::cloneBlockContext, CallResultDestination::Ignore);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CloneBlockContext, CallResultDestination::Ignore);
}
void BaselineJIT::generate_PushScriptContext(int index)
{
as->saveAccumulatorInFrame();
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(index, 2);
- as->passEngineAsArg(1);
- as->passJSSlotAsArg(0, 0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::pushScriptContext, CallResultDestination::Ignore);
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(index, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(PushScriptContext, CallResultDestination::Ignore);
}
void BaselineJIT::generate_PopScriptContext()
{
as->saveAccumulatorInFrame();
- as->prepareCallWithArgCount(2);
- as->passEngineAsArg(1);
- as->passJSSlotAsArg(0, 0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::popScriptContext, CallResultDestination::Ignore);
+ as->prepareCallWithArgCount(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(PopScriptContext, CallResultDestination::Ignore);
}
void BaselineJIT::generate_PopContext() { as->popContext(); }
@@ -630,8 +601,7 @@ void BaselineJIT::generate_GetIterator(int iterator)
as->passInt32AsArg(iterator, 2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_getIterator, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(GetIterator, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_IteratorNext(int value, int done)
@@ -641,9 +611,8 @@ void BaselineJIT::generate_IteratorNext(int value, int done)
as->passJSSlotAsArg(value, 2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorNext, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(IteratorNext, CallResultDestination::InAccumulator);
as->storeReg(done);
- as->checkException();
}
void BaselineJIT::generate_IteratorNextForYieldStar(int iterator, int object)
@@ -654,8 +623,7 @@ void BaselineJIT::generate_IteratorNextForYieldStar(int iterator, int object)
as->passJSSlotAsArg(iterator, 2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorNextForYieldStar, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(IteratorNextForYieldStar, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_IteratorClose(int done)
@@ -665,8 +633,7 @@ void BaselineJIT::generate_IteratorClose(int done)
as->passJSSlotAsArg(done, 2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorClose, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(IteratorClose, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_DestructureRestElement()
@@ -675,29 +642,28 @@ void BaselineJIT::generate_DestructureRestElement()
as->prepareCallWithArgCount(2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_destructureRestElement, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(DestructureRestElement, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_DeleteProperty(int base, int index)
{
STORE_IP();
- as->prepareCallWithArgCount(3);
- as->passJSSlotAsArg(index, 2);
- as->passJSSlotAsArg(base, 1);
- as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::deleteProperty, CallResultDestination::InAccumulator);
- as->checkException();
+ as->prepareCallWithArgCount(4);
+ as->passJSSlotAsArg(index, 3);
+ as->passJSSlotAsArg(base, 2);
+ as->passFunctionAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(DeleteProperty, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_DeleteName(int name)
{
STORE_IP();
- as->prepareCallWithArgCount(2);
- as->passInt32AsArg(name, 1);
- as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::deleteName, CallResultDestination::InAccumulator);
- as->checkException();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(name, 2);
+ as->passFunctionAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(DeleteName, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_TypeofName(int name)
@@ -705,7 +671,7 @@ void BaselineJIT::generate_TypeofName(int name)
as->prepareCallWithArgCount(2);
as->passInt32AsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofName, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(TypeofName, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_TypeofValue()
@@ -714,7 +680,7 @@ void BaselineJIT::generate_TypeofValue()
as->prepareCallWithArgCount(2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofValue, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(TypeofValue, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_DeclareVar(int varName, int isDeletable)
@@ -723,7 +689,7 @@ void BaselineJIT::generate_DeclareVar(int varName, int isDeletable)
as->passInt32AsArg(varName, 2);
as->passInt32AsArg(isDeletable, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_declareVar, CallResultDestination::Ignore);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(DeclareVar, CallResultDestination::Ignore);
}
void BaselineJIT::generate_DefineArray(int argc, int args)
@@ -732,7 +698,7 @@ void BaselineJIT::generate_DefineArray(int argc, int args)
as->passInt32AsArg(argc, 2);
as->passJSSlotAsArg(args, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_arrayLiteral, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ArrayLiteral, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
@@ -742,7 +708,7 @@ void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int argc, in
as->passJSSlotAsArg(args, 2);
as->passInt32AsArg(internalClassId, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_objectLiteral, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ObjectLiteral, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CreateClass(int classIndex, int heritage, int computedNames)
@@ -752,14 +718,14 @@ void BaselineJIT::generate_CreateClass(int classIndex, int heritage, int compute
as->passJSSlotAsArg(heritage, 2);
as->passInt32AsArg(classIndex, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createClass, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CreateClass, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CreateMappedArgumentsObject()
{
as->prepareCallWithArgCount(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createMappedArgumentsObject,
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CreateMappedArgumentsObject,
CallResultDestination::InAccumulator);
}
@@ -767,7 +733,7 @@ void BaselineJIT::generate_CreateUnmappedArgumentsObject()
{
as->prepareCallWithArgCount(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createUnmappedArgumentsObject,
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CreateUnmappedArgumentsObject,
CallResultDestination::InAccumulator);
}
@@ -776,7 +742,7 @@ void BaselineJIT::generate_CreateRestParameter(int argIndex)
as->prepareCallWithArgCount(2);
as->passInt32AsArg(argIndex, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createRestParameter, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CreateRestParameter, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_ConvertThisToObject()
@@ -784,8 +750,8 @@ void BaselineJIT::generate_ConvertThisToObject()
as->prepareCallWithArgCount(2);
as->passJSSlotAsArg(CallData::This, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::convertThisToObject, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ConvertThisToObject, CallResultDestination::InAccumulator);
+ as->storeReg(CallData::This);
}
void BaselineJIT::generate_LoadSuperConstructor()
@@ -793,8 +759,7 @@ void BaselineJIT::generate_LoadSuperConstructor()
as->prepareCallWithArgCount(2);
as->passJSSlotAsArg(CallData::Function, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadSuperConstructor, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadSuperConstructor, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_ToObject()
@@ -803,8 +768,7 @@ void BaselineJIT::generate_ToObject()
as->prepareCallWithArgCount(2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::toObject, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ToObject, CallResultDestination::InAccumulator);
}
@@ -853,8 +817,7 @@ void BaselineJIT::generate_CmpIn(int lhs)
as->passAccumulatorAsArg(2);
as->passJSSlotAsArg(lhs, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_in, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(In, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CmpInstanceOf(int lhs)
@@ -864,12 +827,11 @@ void BaselineJIT::generate_CmpInstanceOf(int lhs)
as->passAccumulatorAsArg(2);
as->passJSSlotAsArg(lhs, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_instanceof, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Instanceof, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_UNot() { as->unot(); }
-void BaselineJIT::generate_UPlus() { as->toNumber(); }
+void BaselineJIT::generate_UPlus(int /*traceSlot*/) { as->toNumber(); }
void BaselineJIT::generate_UMinus(int /*traceSlot*/) { as->uminus(); }
void BaselineJIT::generate_UCompl() { as->ucompl(); }
void BaselineJIT::generate_Increment(int /*traceSlot*/) { as->inc(); }
@@ -896,8 +858,7 @@ void BaselineJIT::generate_Exp(int lhs) {
as->prepareCallWithArgCount(2);
as->passAccumulatorAsArg(1);
as->passJSSlotAsArg(lhs, 0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::exp, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Exp, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_Mul(int lhs, int /*traceSlot*/) { as->mul(lhs); }
void BaselineJIT::generate_Div(int lhs) { as->div(lhs); }
@@ -929,8 +890,7 @@ void BaselineJIT::generate_ThrowOnNullOrUndefined()
as->prepareCallWithArgCount(2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::throwOnNullOrUndefined, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ThrowOnNullOrUndefined, CallResultDestination::Ignore);
}
void BaselineJIT::generate_GetTemplateObject(int index)
@@ -939,14 +899,14 @@ void BaselineJIT::generate_GetTemplateObject(int index)
as->prepareCallWithArgCount(2);
as->passInt32AsArg(index, 1);
as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(RuntimeHelpers::getTemplateObject, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(GetTemplateObject, CallResultDestination::InAccumulator);
}
-void BaselineJIT::startInstruction(Instr::Type /*instr*/)
+ByteCodeHandler::Verdict BaselineJIT::startInstruction(Instr::Type /*instr*/)
{
if (labels.contains(currentInstructionOffset()))
as->addLabel(currentInstructionOffset());
+ return ProcessInstruction;
}
void BaselineJIT::endInstruction(Instr::Type instr)
diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 10c89bc74b..37ab37eac2 100644
--- a/src/qml/jit/qv4baselinejit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -179,7 +179,7 @@ public:
void generate_CmpIn(int lhs) override;
void generate_CmpInstanceOf(int lhs) override;
void generate_UNot() override;
- void generate_UPlus() override;
+ void generate_UPlus(int) override;
void generate_UMinus(int traceSlot) override;
void generate_UCompl() override;
void generate_Increment(int traceSlot) override;
@@ -206,7 +206,7 @@ public:
void generate_ThrowOnNullOrUndefined() override;
void generate_GetTemplateObject(int index) override;
- void startInstruction(Moth::Instr::Type instr) override;
+ Verdict startInstruction(Moth::Instr::Type instr) override;
void endInstruction(Moth::Instr::Type instr) override;
private:
diff --git a/src/qml/jit/qv4blockscheduler.cpp b/src/qml/jit/qv4blockscheduler.cpp
new file mode 100644
index 0000000000..3e2bfe15c5
--- /dev/null
+++ b/src/qml/jit/qv4blockscheduler.cpp
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qloggingcategory.h>
+
+#include "qv4blockscheduler_p.h"
+#include "qv4domtree_p.h"
+#include "qv4loopinfo_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcBlockScheduler, "qt.v4.ir.blockscheduler")
+
+bool BlockScheduler::checkCandidate(MIBlock *candidate)
+{
+ Q_ASSERT(loopInfo.loopHeaderFor(candidate) == currentGroup.group);
+
+ for (MIBlock *pred : candidate->inEdges()) {
+ if (pred->isDeoptBlock())
+ continue;
+
+ if (emitted.alreadyProcessed(pred))
+ continue;
+
+ if (dominatorTree.dominates(candidate->index(), pred->index())) {
+ // this is a loop, where there in
+ // -> candidate edge is the jump back to the top of the loop.
+ continue;
+ }
+
+ if (pred == candidate)
+ // this is a very tight loop, e.g.:
+ // L1: ...
+ // goto L1
+ // This can happen when, for example, the basic-block merging gets rid of the empty
+ // body block. In this case, we can safely schedule this block (if all other
+ // incoming edges are either loop-back edges, or have been scheduled already).
+ continue;
+
+ return false; // an incoming edge that is not yet emitted, and is not a back-edge
+ }
+
+ if (loopInfo.isLoopHeader(candidate)) {
+ // postpone everything, and schedule the loop first.
+ postponedGroups.push(currentGroup);
+ currentGroup = WorkForGroup(candidate);
+ }
+
+ return true;
+}
+
+MIBlock *BlockScheduler::pickNext()
+{
+ while (true) {
+ while (currentGroup.postponed.isEmpty()) {
+ if (postponedGroups.isEmpty())
+ return nullptr;
+ if (currentGroup.group) // record the first and the last node of a group
+ loopsStartEnd[currentGroup.group] = sequence.back();
+ currentGroup = postponedGroups.pop();
+ }
+
+ MIBlock *next = currentGroup.postponed.pop();
+ if (checkCandidate(next))
+ return next;
+ }
+
+ Q_UNREACHABLE();
+ return nullptr;
+}
+
+void BlockScheduler::emitBlock(MIBlock *bb)
+{
+ if (emitted.alreadyProcessed(bb))
+ return;
+
+ sequence.push_back(bb);
+ emitted.markAsProcessed(bb);
+}
+
+void BlockScheduler::schedule(MIBlock *functionEntryPoint)
+{
+ MIBlock *next = functionEntryPoint;
+
+ while (next) {
+ emitBlock(next);
+ // postpone all outgoing edges, if they were not already processed
+ QVarLengthArray<MIBlock *, 32> nonExceptionEdges;
+ // first postpone all exception edges, so they will be processed last
+ for (int i = next->outEdges().size(); i != 0; ) {
+ --i;
+ MIBlock *out = next->outEdges().at(i);
+ if (emitted.alreadyProcessed(out))
+ continue;
+ if (out == nullptr)
+ continue;
+ if (out->instructions().front().opcode() == Meta::OnException)
+ postpone(out);
+ else
+ nonExceptionEdges.append(out);
+ }
+ for (MIBlock *edge : nonExceptionEdges)
+ postpone(edge);
+ next = pickNext();
+ }
+
+ // finally schedule all de-optimization blocks at the end
+ for (auto bb : dominatorTree.function()->blocks()) {
+ if (bb->isDeoptBlock())
+ emitBlock(bb);
+ }
+}
+
+void BlockScheduler::postpone(MIBlock *bb)
+{
+ if (currentGroup.group == loopInfo.loopHeaderFor(bb)) {
+ currentGroup.postponed.append(bb);
+ return;
+ }
+
+ for (int i = postponedGroups.size(); i != 0; ) {
+ --i;
+ WorkForGroup &g = postponedGroups[i];
+ if (g.group == loopInfo.loopHeaderFor(bb)) {
+ g.postponed.append(bb);
+ return;
+ }
+ }
+
+ Q_UNREACHABLE();
+}
+
+void BlockScheduler::dump() const
+{
+ if (!lcBlockScheduler().isDebugEnabled())
+ return;
+
+ QString s = QStringLiteral("Scheduled blocks:\n");
+ for (auto *bb : sequence) {
+ s += QLatin1String(" L") + QString::number(bb->index());
+ MIBlock *loopEnd = loopsStartEnd[bb];
+ if (loopEnd)
+ s += QLatin1String(", loop start, ends at L") + QString::number(loopEnd->index());
+ s += QLatin1Char('\n');
+ }
+ qCDebug(lcBlockScheduler).noquote().nospace() << s;
+}
+
+BlockScheduler::BlockScheduler(const DominatorTree &dominatorTree, const LoopInfo &loopInfo)
+ : dominatorTree(dominatorTree)
+ , loopInfo(loopInfo)
+ , sequence(0)
+ , emitted(dominatorTree.function())
+{
+ schedule(dominatorTree.function()->blocks().front());
+
+ dump();
+
+ if (dominatorTree.function()->blockCount() != sequence.size()) {
+ qFatal("The block scheduler did not schedule all blocks. This is most likely due to"
+ "a non-natural loop.");
+ // Usually caused by having an execution path that manages to skip over unwind handler
+ // reset: any exception happening after will jump back to the unwind handler, and thereby
+ // creating a loop that can be entered in 2 different ways.
+ }
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4blockscheduler_p.h b/src/qml/jit/qv4blockscheduler_p.h
new file mode 100644
index 0000000000..1289fda9f0
--- /dev/null
+++ b/src/qml/jit/qv4blockscheduler_p.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4BLOCKSCHEDULER_P_H
+#define QV4BLOCKSCHEDULER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstack.h>
+
+#include "qv4mi_p.h"
+#include "qv4util_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class DominatorTree;
+class LoopInfo;
+
+// High-level algorithm:
+// 0. start with the first node (the start node) of a function
+// 1. emit the node
+// 2. add all outgoing edges that are not yet emitted to the postponed stack
+// 3. When the postponed stack is empty, pop a stack from the loop stack. If that is empty too,
+// we're done.
+// 4. pop a node from the postponed stack, and check if it can be scheduled:
+// a. if all incoming edges are scheduled, go to 4.
+// b. if an incoming edge is unscheduled, but it's a back-edge (an edge in a loop that jumps
+// back to the start of the loop), ignore it
+// c. if there is any unscheduled edge that is not a back-edge, ignore this node, and go to 4.
+// 5. if this node is the start of a loop, push the postponed stack on the loop stack.
+// 6. go back to 1.
+//
+// The postponing action in step 2 will put the node into its containing group. The case where this
+// is important is when a (labeled) continue or a (labeled) break statement occur in a loop: the
+// outgoing edge points to a node that is not part of the current loop (and possibly not of the
+// parent loop).
+//
+// Linear scan register allocation benefits greatly from short life-time intervals with few holes
+// (see for example section 4 (Lifetime Analysis) of [Wimmer1]). This algorithm makes sure that the
+// blocks of a group are scheduled together, with no non-loop blocks in between. This applies
+// recursively for nested loops. It also schedules groups of if-then-else-endif blocks together for
+// the same reason.
+class BlockScheduler
+{
+ const DominatorTree &dominatorTree;
+ const LoopInfo &loopInfo;
+
+ struct WorkForGroup
+ {
+ MIBlock *group;
+ QStack<MIBlock *> postponed;
+
+ WorkForGroup(MIBlock *group = nullptr) : group(group) {}
+ };
+ WorkForGroup currentGroup;
+ QStack<WorkForGroup> postponedGroups;
+ std::vector<MIBlock *> sequence;
+
+ class ProcessedBlocks
+ {
+ BitVector processed;
+
+ public:
+ ProcessedBlocks(MIFunction *function)
+ : processed(int(function->blockCount()), false)
+ {}
+
+ bool alreadyProcessed(MIBlock *bb) const
+ {
+ Q_ASSERT(bb);
+
+ return processed.at(bb->index());
+ }
+
+ void markAsProcessed(MIBlock *bb)
+ {
+ processed.setBit(bb->index());
+ }
+ } emitted;
+ QHash<MIBlock *, MIBlock *> loopsStartEnd;
+
+ bool checkCandidate(MIBlock *candidate);
+ MIBlock *pickNext();
+ void emitBlock(MIBlock *bb);
+ void schedule(MIBlock *functionEntryPoint);
+ void postpone(MIBlock *bb);
+ void dump() const;
+
+public:
+ BlockScheduler(const DominatorTree &dominatorTree, const LoopInfo &loopInfo);
+
+ const std::vector<MIBlock *> &scheduledBlockSequence() const
+ { return sequence; }
+
+ QHash<MIBlock *, MIBlock *> loopEndsByStartBlock() const
+ { return loopsStartEnd; }
+};
+
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4BLOCKSCHEDULER_P_H
diff --git a/src/qml/jit/qv4domtree.cpp b/src/qml/jit/qv4domtree.cpp
new file mode 100644
index 0000000000..9484f4e2dc
--- /dev/null
+++ b/src/qml/jit/qv4domtree.cpp
@@ -0,0 +1,436 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qbuffer.h>
+
+#include "qv4domtree_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcDomTree, "qt.v4.ir.domTree")
+Q_LOGGING_CATEGORY(lcDomFrontier, "qt.v4.ir.domFrontier")
+
+DominatorTree::DominatorTree(MIFunction *f)
+ : m_function(f)
+ , m_data(new Data)
+{
+ calculateIDoms();
+ m_data.reset();
+}
+
+void DominatorTree::dumpImmediateDominators() const
+{
+ QBuffer buf;
+ buf.open(QIODevice::WriteOnly);
+ QTextStream qout(&buf);
+ qout << "Immediate dominators for " << m_function->irFunction()->name() << ":" << endl;
+ for (MIBlock *to : m_function->blocks()) {
+ MIBlock::Index from = m_idom.at(to->index());
+ if (from != MIBlock::InvalidIndex)
+ qout << " " << from;
+ else
+ qout << " (none)";
+ qout << " dominates " << to->index() << endl;
+ }
+ qCDebug(lcDomTree, "%s", buf.data().constData());
+}
+
+void DominatorTree::setImmediateDominator(MIBlock::Index dominated, MIBlock::Index dominator)
+{
+ if (m_idom.size() <= size_t(dominated))
+ m_idom.resize(dominated + 1);
+ m_idom[dominated] = dominator;
+}
+
+bool DominatorTree::dominates(MIBlock::Index dominator, MIBlock::Index dominated) const
+{
+ // dominator can be Invalid when the dominated block has no dominator (i.e. the start node)
+ Q_ASSERT(dominated != MIBlock::InvalidIndex);
+
+ if (dominator == dominated)
+ return false;
+
+ for (MIBlock::Index it = m_idom[dominated]; it != MIBlock::InvalidIndex; it = m_idom[it]) {
+ if (it == dominator)
+ return true;
+ }
+
+ return false;
+}
+
+// Calculate a depth-first iteration order on the nodes of the dominator tree.
+//
+// The order of the nodes in the vector is not the same as one where a recursive depth-first
+// iteration is done on a tree. Rather, the nodes are (reverse) sorted on tree depth.
+// So for the:
+// 1 dominates 2
+// 2 dominates 3
+// 3 dominates 4
+// 2 dominates 5
+// the order will be:
+// 4, 3, 5, 2, 1
+// or:
+// 4, 5, 3, 2, 1
+// So the order of nodes on the same depth is undefined, but it will be after the nodes
+// they dominate, and before the nodes that dominate them.
+//
+// The reason for this order is that a proper DFS pre-/post-order would require inverting
+// the idom vector by either building a real tree datastructure or by searching the idoms
+// for siblings and children. Both have a higher time complexity than sorting by depth.
+std::vector<MIBlock *> DominatorTree::calculateDFNodeIterOrder() const
+{
+ std::vector<int> depths = calculateNodeDepths();
+ std::vector<MIBlock *> order = m_function->blocks();
+ std::sort(order.begin(), order.end(), [&depths](MIBlock *one, MIBlock *two) -> bool {
+ return depths.at(one->index()) > depths.at(two->index());
+ });
+ return order;
+}
+
+// Algorithm:
+// - for each node:
+// - get the depth of a node. If it's unknown (-1):
+// - get the depth of the immediate dominator.
+// - if that's unknown too, calculate it by calling calculateNodeDepth
+// - set the current node's depth to that of immediate dominator + 1
+std::vector<int> DominatorTree::calculateNodeDepths() const
+{
+ std::vector<int> nodeDepths(size_t(m_function->blockCount()), -1);
+ for (MIBlock *bb : m_function->blocks()) {
+ int &bbDepth = nodeDepths[bb->index()];
+ if (bbDepth == -1) {
+ const int immDom = m_idom[bb->index()];
+ if (immDom == -1) {
+ // no immediate dominator, so it's either the start block, or an unreachable block
+ bbDepth = 0;
+ } else {
+ int immDomDepth = nodeDepths[immDom];
+ if (immDomDepth == -1)
+ immDomDepth = calculateNodeDepth(immDom, nodeDepths);
+ bbDepth = immDomDepth + 1;
+ }
+ }
+ }
+ return nodeDepths;
+}
+
+// Algorithm:
+// - search for the first dominator of a node that has a known depth. As all nodes are
+// reachable from the start node, and that node's depth is 0, this is finite.
+// - while doing that search, put all unknown nodes in the worklist
+// - pop all nodes from the worklist, and set their depth to the previous' (== dominating)
+// node's depth + 1
+// This way every node's depth is calculated once, and the complexity is O(n).
+int DominatorTree::calculateNodeDepth(MIBlock::Index nodeIdx, std::vector<int> &nodeDepths) const
+{
+ std::vector<int> worklist;
+ worklist.reserve(8);
+ int depth = -1;
+
+ do {
+ worklist.push_back(nodeIdx);
+ nodeIdx = m_idom[nodeIdx];
+ depth = nodeDepths[nodeIdx];
+ } while (depth == -1);
+
+ for (auto it = worklist.rbegin(), eit = worklist.rend(); it != eit; ++it)
+ nodeDepths[*it] = ++depth;
+
+ return depth;
+}
+
+namespace {
+struct DFSTodo {
+ MIBlock::Index node = MIBlock::InvalidIndex;
+ MIBlock::Index parent = MIBlock::InvalidIndex;
+
+ DFSTodo() = default;
+ DFSTodo(MIBlock::Index node, MIBlock::Index parent)
+ : node(node)
+ , parent(parent)
+ {}
+};
+} // anonymous namespace
+
+void DominatorTree::dfs(MIBlock::Index node)
+{
+ std::vector<DFSTodo> worklist;
+ worklist.reserve(m_data->vertex.capacity() / 2);
+ DFSTodo todo(node, MIBlock::InvalidIndex);
+
+ while (true) {
+ MIBlock::Index n = todo.node;
+
+ if (m_data->dfnum[n] == 0) {
+ m_data->dfnum[n] = m_data->size;
+ m_data->vertex[m_data->size] = n;
+ m_data->parent[n] = todo.parent;
+ ++m_data->size;
+
+ MIBlock::OutEdges out = m_function->block(n)->outEdges();
+ for (int i = out.size() - 1; i > 0; --i)
+ worklist.emplace_back(out[i]->index(), n);
+
+ if (!out.isEmpty()) {
+ todo.node = out.first()->index();
+ todo.parent = n;
+ continue;
+ }
+ }
+
+ if (worklist.empty())
+ break;
+
+ todo = worklist.back();
+ worklist.pop_back();
+ }
+}
+
+void DominatorTree::link(MIBlock::Index p, MIBlock::Index n)
+{
+ m_data->ancestor[n] = p;
+ m_data->best[n] = n;
+}
+
+void DominatorTree::calculateIDoms()
+{
+ Q_ASSERT(m_function->block(0)->inEdges().count() == 0);
+
+ const size_t bbCount = m_function->blockCount();
+ m_data->vertex = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->parent = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->dfnum = std::vector<unsigned>(bbCount, 0);
+ m_data->semi = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->ancestor = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_idom = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->samedom = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->best = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+
+ QHash<MIBlock::Index, std::vector<MIBlock::Index>> bucket;
+ bucket.reserve(int(bbCount));
+
+ dfs(m_function->block(0)->index());
+
+ std::vector<MIBlock::Index> worklist;
+ worklist.reserve(m_data->vertex.capacity() / 2);
+
+ for (int i = m_data->size - 1; i > 0; --i) {
+ MIBlock::Index n = m_data->vertex[i];
+ MIBlock::Index p = m_data->parent[n];
+ MIBlock::Index s = p;
+
+ for (auto inEdge : m_function->block(n)->inEdges()) {
+ if (inEdge->isDeoptBlock())
+ continue;
+ MIBlock::Index v = inEdge->index();
+ MIBlock::Index ss = MIBlock::InvalidIndex;
+ if (m_data->dfnum[v] <= m_data->dfnum[n])
+ ss = v;
+ else
+ ss = m_data->semi[ancestorWithLowestSemi(v, worklist)];
+ if (m_data->dfnum[ss] < m_data->dfnum[s])
+ s = ss;
+ }
+ m_data->semi[n] = s;
+ bucket[s].push_back(n);
+ link(p, n);
+ if (bucket.contains(p)) {
+ for (MIBlock::Index v : bucket[p]) {
+ MIBlock::Index y = ancestorWithLowestSemi(v, worklist);
+ MIBlock::Index semi_v = m_data->semi[v];
+ if (m_data->semi[y] == semi_v)
+ m_idom[v] = semi_v;
+ else
+ m_data->samedom[v] = y;
+ }
+ bucket.remove(p);
+ }
+ }
+
+ for (unsigned i = 1; i < m_data->size; ++i) {
+ MIBlock::Index n = m_data->vertex[i];
+ Q_ASSERT(n != MIBlock::InvalidIndex);
+ Q_ASSERT(!bucket.contains(n));
+ Q_ASSERT(m_data->ancestor[n] != MIBlock::InvalidIndex);
+ Q_ASSERT((m_data->semi[n] != MIBlock::InvalidIndex
+ && m_data->dfnum[m_data->ancestor[n]] <= m_data->dfnum[m_data->semi[n]])
+ || m_data->semi[n] == n);
+ MIBlock::Index sdn = m_data->samedom[n];
+ if (sdn != MIBlock::InvalidIndex)
+ m_idom[n] = m_idom[sdn];
+ }
+
+ if (lcDomTree().isDebugEnabled())
+ dumpImmediateDominators();
+
+ m_data.reset(nullptr);
+}
+
+MIBlock::Index DominatorTree::ancestorWithLowestSemi(MIBlock::Index v,
+ std::vector<MIBlock::Index> &worklist)
+{
+ worklist.clear();
+ for (MIBlock::Index it = v; it != MIBlock::InvalidIndex; it = m_data->ancestor[it])
+ worklist.push_back(it);
+
+ if (worklist.size() < 2)
+ return m_data->best[v];
+
+ MIBlock::Index b = MIBlock::InvalidIndex;
+ MIBlock::Index last = worklist.back();
+ Q_ASSERT(worklist.size() <= INT_MAX);
+ for (int it = static_cast<int>(worklist.size()) - 2; it >= 0; --it) {
+ MIBlock::Index bbIt = worklist[it];
+ m_data->ancestor[bbIt] = last;
+ MIBlock::Index &best_it = m_data->best[bbIt];
+ if (b != MIBlock::InvalidIndex
+ && m_data->dfnum[m_data->semi[b]] < m_data->dfnum[m_data->semi[best_it]]) {
+ best_it = b;
+ } else {
+ b = best_it;
+ }
+ }
+ return b;
+}
+
+void DominatorFrontier::compute(const DominatorTree &domTree)
+{
+ struct NodeProgress {
+ std::vector<MIBlock::Index> children;
+ std::vector<MIBlock::Index> todo;
+ };
+
+ MIFunction *function = domTree.function();
+ m_df.resize(function->blockCount());
+
+ // compute children of each node in the dominator tree
+ std::vector<std::vector<MIBlock::Index> > children; // BasicBlock index -> children
+ children.resize(function->blockCount());
+ for (MIBlock *n : function->blocks()) {
+ const MIBlock::Index nodeIndex = n->index();
+ Q_ASSERT(function->block(nodeIndex) == n);
+ const MIBlock::Index nodeDominator = domTree.immediateDominator(nodeIndex);
+ if (nodeDominator == MIBlock::InvalidIndex)
+ continue; // there is no dominator to add this node to as a child (e.g. the start node)
+ children[nodeDominator].push_back(nodeIndex);
+ }
+
+ // Fill the worklist and initialize the node status for each basic-block
+ std::vector<NodeProgress> nodeStatus;
+ nodeStatus.resize(function->blockCount());
+ std::vector<MIBlock::Index> worklist;
+ worklist.reserve(function->blockCount());
+ for (MIBlock *bb : function->blocks()) {
+ MIBlock::Index nodeIndex = bb->index();
+ worklist.push_back(nodeIndex);
+ NodeProgress &np = nodeStatus[nodeIndex];
+ np.children = children[nodeIndex];
+ np.todo = children[nodeIndex];
+ }
+
+ BitVector DF_done(int(function->blockCount()), false);
+
+ while (!worklist.empty()) {
+ MIBlock::Index node = worklist.back();
+
+ if (DF_done.at(node)) {
+ worklist.pop_back();
+ continue;
+ }
+
+ NodeProgress &np = nodeStatus[node];
+ auto it = np.todo.begin();
+ while (it != np.todo.end()) {
+ if (DF_done.at(*it)) {
+ it = np.todo.erase(it);
+ } else {
+ worklist.push_back(*it);
+ break;
+ }
+ }
+
+ if (np.todo.empty()) {
+ MIBlockSet &miBlockSet = m_df[node];
+ miBlockSet.init(function);
+ for (MIBlock *y : function->block(node)->outEdges()) {
+ if (domTree.immediateDominator(y->index()) != node)
+ miBlockSet.insert(y);
+ }
+ for (MIBlock::Index child : np.children) {
+ const MIBlockSet &ws = m_df[child];
+ for (auto w : ws) {
+ const MIBlock::Index wIndex = w->index();
+ if (node == wIndex || !domTree.dominates(node, w->index()))
+ miBlockSet.insert(w);
+ }
+ }
+ DF_done.setBit(node);
+ worklist.pop_back();
+ }
+ }
+
+ if (lcDomFrontier().isDebugEnabled())
+ dump(domTree.function());
+}
+
+void DominatorFrontier::dump(MIFunction *function)
+{
+ QBuffer buf;
+ buf.open(QIODevice::WriteOnly);
+ QTextStream qout(&buf);
+ qout << "Dominator Frontiers:" << endl;
+ for (MIBlock *n : function->blocks()) {
+ qout << "\tDF[" << n->index() << "]: {";
+ const MIBlockSet &SList = m_df[n->index()];
+ for (MIBlockSet::const_iterator i = SList.begin(), ei = SList.end(); i != ei; ++i) {
+ if (i != SList.begin())
+ qout << ", ";
+ qout << (*i)->index();
+ }
+ qout << "}" << endl;
+ }
+ qCDebug(lcDomFrontier, "%s", buf.data().constData());
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4domtree_p.h b/src/qml/jit/qv4domtree_p.h
new file mode 100644
index 0000000000..703e17ab61
--- /dev/null
+++ b/src/qml/jit/qv4domtree_p.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4DOMTREE_P_H
+#define QV4DOMTREE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4mi_p.h"
+#include "qv4miblockset_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class DominatorTree
+{
+ Q_DISABLE_COPY_MOVE(DominatorTree)
+
+public:
+ DominatorTree(MIFunction *f);
+ ~DominatorTree() = default;
+
+ void dumpImmediateDominators() const;
+ MIFunction *function() const
+ { return m_function; }
+
+ void setImmediateDominator(MIBlock::Index dominated, MIBlock::Index dominator);
+
+ MIBlock::Index immediateDominator(MIBlock::Index blockIndex) const
+ { return m_idom[blockIndex]; }
+
+ bool dominates(MIBlock::Index dominator, MIBlock::Index dominated) const;
+
+ bool insideSameDominatorChain(MIBlock::Index one, MIBlock::Index other) const
+ { return one == other || dominates(one, other) || dominates(other, one); }
+
+ std::vector<MIBlock *> calculateDFNodeIterOrder() const;
+
+ std::vector<int> calculateNodeDepths() const;
+
+private: // functions
+ int calculateNodeDepth(MIBlock::Index nodeIdx, std::vector<int> &nodeDepths) const;
+ void link(MIBlock::Index p, MIBlock::Index n);
+ void calculateIDoms();
+ void dfs(MIBlock::Index node);
+ MIBlock::Index ancestorWithLowestSemi(MIBlock::Index v, std::vector<MIBlock::Index> &worklist);
+
+private: // data
+ struct Data {
+ std::vector<unsigned> dfnum; // MIBlock index -> dfnum
+ std::vector<MIBlock::Index> vertex;
+ std::vector<MIBlock::Index> parent; // MIBlock index -> parent MIBlock index
+ std::vector<MIBlock::Index> ancestor; // MIBlock index -> ancestor MIBlock index
+ std::vector<MIBlock::Index> best; // MIBlock index -> best MIBlock index
+ std::vector<MIBlock::Index> semi; // MIBlock index -> semi dominator MIBlock index
+ std::vector<MIBlock::Index> samedom; // MIBlock index -> same dominator MIBlock index
+ unsigned size = 0;
+ };
+
+ MIFunction *m_function;
+ QScopedPointer<Data> m_data;
+ std::vector<MIBlock::Index> m_idom; // MIBlock index -> immediate dominator MIBlock index
+};
+
+class DominatorFrontier
+{
+public:
+ DominatorFrontier(const DominatorTree &domTree)
+ { compute(domTree); }
+
+ const MIBlockSet &operator[](MIBlock *n) const
+ { return m_df[n->index()]; }
+
+private: // functions
+ void compute(const DominatorTree &domTree);
+ void dump(MIFunction *function);
+
+private: // data
+ std::vector<MIBlockSet> m_df; // MIBlock index -> dominator frontier
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4DOMTREE_P_H
diff --git a/src/qml/jit/qv4graph.cpp b/src/qml/jit/qv4graph.cpp
new file mode 100644
index 0000000000..4025ceb993
--- /dev/null
+++ b/src/qml/jit/qv4graph.cpp
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4graph_p.h"
+#include "qv4operation_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Graph *Graph::create(Function *function)
+{
+ auto storage = function->pool()->allocate(sizeof(Graph));
+ auto g = new (storage) Graph(function);
+ g->m_undefinedNode = g->createNode(g->opBuilder()->get<Meta::Undefined>());
+ g->m_emptyNode = g->createNode(g->opBuilder()->get<Meta::Empty>());
+ g->m_nullNode = g->createNode(g->opBuilder()->getConstant(QV4::Value::nullValue()));
+ g->m_trueNode = g->createNode(g->opBuilder()->getConstant(QV4::Value::fromBoolean(true)));
+ g->m_falseNode = g->createNode(g->opBuilder()->getConstant(QV4::Value::fromBoolean(false)));
+ return g;
+}
+
+Graph::MemoryPool *Graph::pool() const
+{
+ return m_function->pool();
+}
+
+Node *Graph::createNode(const Operation *op, Node *const operands[], size_t opCount,
+ bool incomplete)
+{
+ return Node::create(pool(), m_nextNodeId++, op, opCount, operands, incomplete);
+}
+
+Node *Graph::createConstantBoolNode(bool value)
+{
+ return createNode(opBuilder()->getConstant(Primitive::fromBoolean(value)));
+}
+
+Node *Graph::createConstantIntNode(int value)
+{
+ return createNode(opBuilder()->getConstant(Primitive::fromInt32(value)));
+}
+
+Graph::Graph(Function *function)
+ : m_function(function)
+ , m_opBuilder(OperationBuilder::create(pool()))
+{}
+
+Node *Graph::createConstantHeapNode(Heap::Base *heap)
+{
+ return createNode(opBuilder()->getConstant(Primitive::fromHeapObject(heap)));
+}
+
+void Graph::addEndInput(Node *n)
+{
+ if (m_endNode) {
+ auto newEnd = m_opBuilder->getEnd(m_endNode->operation()->controlInputCount() + 1);
+ m_endNode->setOperation(newEnd);
+ m_endNode->addInput(m_function->pool(), n);
+ }
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4graph_p.h b/src/qml/jit/qv4graph_p.h
new file mode 100644
index 0000000000..4706399c94
--- /dev/null
+++ b/src/qml/jit/qv4graph_p.h
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4GRAPH_P_H
+#define QV4GRAPH_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qqmljsmemorypool_p.h>
+#include <private/qv4global_p.h>
+#include <private/qv4node_p.h>
+
+#include <array>
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class Function;
+class Operation;
+class OperationBuilder;
+
+class Graph final
+{
+ Q_DISABLE_COPY_MOVE(Graph)
+
+public:
+ using MemoryPool = QQmlJS::MemoryPool;
+
+public:
+ static Graph *create(Function *function);
+ ~Graph() = delete;
+
+ MemoryPool *pool() const;
+ OperationBuilder *opBuilder() const
+ { return m_opBuilder; }
+
+ Node *createNode(const Operation *op, Node * const operands[] = nullptr, size_t opCount = 0,
+ bool incomplete = false);
+ template <typename... Nodes>
+ Node *createNode(Operation *op, Nodes*... nodes) {
+ std::array<Node *, sizeof...(nodes)> nodesArray {{ nodes... }};
+ return createNode(op, nodesArray.data(), nodesArray.size());
+ }
+ Node *createConstantBoolNode(bool value);
+ Node *createConstantIntNode(int value);
+ Node *createConstantHeapNode(Heap::Base *heap);
+
+ Node *undefinedNode() const { return m_undefinedNode; }
+ Node *emptyNode() const { return m_emptyNode; }
+ Node *nullNode() const { return m_nullNode; }
+ Node *trueConstant() const { return m_trueNode; }
+ Node *falseConstant() const { return m_falseNode; }
+
+ Node *startNode() const { return m_startNode; }
+ Node *engineNode() const { return m_engineNode; }
+ Node *functionNode() const { return m_functionNode; }
+ Node *cppFrameNode() const { return m_cppFrameNode; }
+ Node *endNode() const { return m_endNode; }
+ Node *initialFrameState() const { return m_initialFrameState; }
+ void setStartNode(Node *n) { m_startNode = n; }
+ void setEngineNode(Node *n) { m_engineNode = n; }
+ void setFunctionNode(Node *n) { m_functionNode = n; }
+ void setCppFrameNode(Node *n) { m_cppFrameNode = n; }
+ void setEndNode(Node *n) { m_endNode = n; }
+ void setInitialFrameState(Node *n) { m_initialFrameState = n; }
+
+ unsigned nodeCount() const
+ { return unsigned(m_nextNodeId); }
+
+ void addEndInput(Node *n);
+
+private: // types and methods
+ Graph(Function *function);
+
+private: // fields
+ Function *m_function;
+ OperationBuilder *m_opBuilder;
+ Node::Id m_nextNodeId = 0;
+ Node *m_undefinedNode = nullptr;
+ Node *m_emptyNode = nullptr;
+ Node *m_nullNode = nullptr;
+ Node *m_trueNode = nullptr;
+ Node *m_falseNode = nullptr;
+ Node *m_startNode = nullptr;
+ Node *m_engineNode = nullptr;
+ Node *m_functionNode = nullptr;
+ Node *m_cppFrameNode = nullptr;
+ Node *m_endNode = nullptr;
+ Node *m_initialFrameState = nullptr;
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4GRAPH_P_H
diff --git a/src/qml/jit/qv4graphbuilder.cpp b/src/qml/jit/qv4graphbuilder.cpp
new file mode 100644
index 0000000000..2c073701ee
--- /dev/null
+++ b/src/qml/jit/qv4graphbuilder.cpp
@@ -0,0 +1,1683 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qloggingcategory.h>
+
+#include "qv4graphbuilder_p.h"
+#include "qv4function_p.h"
+#include "qv4lookup_p.h"
+#include "qv4stackframe_p.h"
+#include "qv4operation_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcIRGraphBuilder, "qt.v4.ir.graphbuilder")
+
+using MemoryPool = QQmlJS::MemoryPool;
+
+namespace {
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+template <typename T, size_t N>
+char (&ArraySizeHelper(const T (&array)[N]))[N];
+
+template <typename Array>
+inline size_t arraySize(const Array &array)
+{
+ Q_UNUSED(array); // for MSVC
+ return sizeof(ArraySizeHelper(array));
+}
+} // anonymous namespace
+
+class GraphBuilder::InterpreterEnvironment
+{
+public:
+ struct FrameState: public QQmlJS::FixedPoolArray<Node *>
+ {
+ FrameState(MemoryPool *pool, int totalSlotCount)
+ : FixedPoolArray(pool, totalSlotCount)
+ {}
+
+ Node *&unwindHandlerOffset()
+ { return at(size() - 1); }
+
+ static FrameState *create(MemoryPool *pool, int jsSlotCount)
+ {
+ auto totalSlotCount = jsSlotCount;
+ auto fs = pool->New<FrameState>(pool, totalSlotCount);
+ return fs;
+ }
+
+ static FrameState *clone(MemoryPool *pool, FrameState *other)
+ {
+ FrameState *fs = create(pool, other->size());
+
+ for (int i = 0, ei = other->size(); i != ei; ++i)
+ fs->at(i) = other->at(i);
+
+ return fs;
+ }
+ };
+
+public:
+ InterpreterEnvironment(GraphBuilder *graphBuilder, Node *controlDependency)
+ : m_graphBuilder(graphBuilder)
+ , m_effectDependency(controlDependency)
+ , m_controlDependency(controlDependency)
+ , m_currentFrame(nullptr)
+ {}
+
+ void createEnvironment()
+ {
+ Function *f = function();
+ QV4::Function *v4Function = f->v4Function();
+ const size_t nRegisters = v4Function->compiledFunction->nRegisters;
+
+ // 1 extra slot for the unwindHandlerOffset
+ m_currentFrame = FrameState::create(graph()->pool(), int(nRegisters + 1));
+ }
+
+ void setupStartEnvironment()
+ {
+ Function *f = function();
+ QV4::Function *v4Function = f->v4Function();
+ const size_t nFormals = v4Function->compiledFunction->nFormals;
+ const size_t nRegisters = v4Function->compiledFunction->nRegisters;
+
+ createEnvironment();
+
+ Node *startNode = graph()->startNode();
+ auto opB = opBuilder();
+ auto create = [&](int index, const char *name) {
+ m_currentFrame->at(index) = graph()->createNode(
+ opB->getParam(index, f->addString(QLatin1String(name))), startNode);
+ };
+ create(0, "%function");
+ create(1, "%context");
+ create(2, "%acc");
+ create(3, "%this");
+ create(4, "%newTarget");
+ create(5, "%argc");
+ const quint32_le *formalNameIdx = v4Function->compiledFunction->formalsTable();
+ for (size_t i = 0; i < nFormals; ++i, ++formalNameIdx) {
+ const int slot = int(CallData::HeaderSize() + i);
+ Q_ASSERT(*formalNameIdx <= quint32(std::numeric_limits<int>::max()));
+ auto op = opB->getParam(
+ slot,
+ f->addString(v4Function->compilationUnit->stringAt(int(*formalNameIdx))));
+ Node *argNode = graph()->createNode(op, startNode);
+ m_currentFrame->at(slot) = argNode;
+ }
+ Node *undefinedNode = graph()->undefinedNode();
+ Node *emptyNode = graph()->emptyNode();
+ const auto firstDeadZoneRegister
+ = v4Function->compiledFunction->firstTemporalDeadZoneRegister;
+ const auto registerDeadZoneSize
+ = v4Function->compiledFunction->sizeOfRegisterTemporalDeadZone;
+ for (size_t i = CallData::HeaderSize() + nFormals; i < nRegisters; ++i) {
+ const bool isDead = i >= firstDeadZoneRegister
+ && i < size_t(firstDeadZoneRegister + registerDeadZoneSize);
+ m_currentFrame->at(int(i)) = isDead ? emptyNode : undefinedNode;
+ }
+ setUnwindHandlerOffset(0);
+ }
+
+ Function *function() const { return m_graphBuilder->function(); }
+ Graph *graph() const { return function()->graph(); }
+ OperationBuilder *opBuilder() const { return graph()->opBuilder(); }
+ GraphBuilder *graphBuilder() const { return m_graphBuilder; }
+
+ Node *bindAcc(Node *node)
+ {
+ bindNodeToSlot(node, CallData::Accumulator);
+ return node;
+ }
+
+ Node *accumulator() const
+ { return slot(CallData::Accumulator); }
+
+ Node *bindNodeToSlot(Node *node, int slot)
+ {
+ m_currentFrame->at(size_t(slot)) = node;
+ return node;
+ }
+
+ Node *slot(int slot) const
+ { return m_currentFrame->at(slot); }
+
+ int slotCount() const
+ { return m_currentFrame->size(); }
+
+ Node *effectDependency() const
+ { return m_effectDependency; }
+
+ void setEffectDependency(Node *newNode)
+ { m_effectDependency = newNode; }
+
+ Node *controlDependency() const
+ { return m_controlDependency; }
+
+ void setControlDependency(Node *newNode)
+ { m_controlDependency = newNode; }
+
+ Node *createFrameState()
+ {
+ return graph()->createNode(graphBuilder()->opBuilder()->getFrameState(slotCount()),
+ m_currentFrame->begin(), slotCount());
+ }
+
+ Node *merge(InterpreterEnvironment *other);
+
+ InterpreterEnvironment *copy() const
+ {
+ auto *newEnv = graph()->pool()->New<InterpreterEnvironment>(graphBuilder(),
+ controlDependency());
+ newEnv->setEffectDependency(effectDependency());
+ newEnv->m_currentFrame = FrameState::clone(graph()->pool(), m_currentFrame);
+ return newEnv;
+ }
+
+ int unwindHandlerOffset() const
+ {
+ auto uhOp = m_currentFrame->unwindHandlerOffset()->operation();
+ Q_ASSERT(uhOp->kind() == Meta::Constant);
+ return ConstantPayload::get(*uhOp)->value().int_32();
+ }
+
+ void setUnwindHandlerOffset(int newOffset)
+ { m_currentFrame->unwindHandlerOffset() = graphBuilder()->createConstant(newOffset); }
+
+ FrameState *frameState() const
+ { return m_currentFrame; }
+
+private:
+ GraphBuilder *m_graphBuilder;
+ Node *m_effectDependency;
+ Node *m_controlDependency;
+ FrameState *m_currentFrame;
+};
+
+namespace {
+class InterpreterSubEnvironment final
+{
+ Q_DISABLE_COPY_MOVE(InterpreterSubEnvironment)
+
+public:
+ explicit InterpreterSubEnvironment(GraphBuilder *builder)
+ : m_builder(builder)
+ , m_parent(builder->env()->copy())
+ {}
+
+ ~InterpreterSubEnvironment()
+ { m_builder->setEnv(m_parent); }
+
+private:
+ GraphBuilder *m_builder;
+ GraphBuilder::InterpreterEnvironment *m_parent;
+};
+} // anonymous namespace
+
+Node *GraphBuilder::InterpreterEnvironment::merge(InterpreterEnvironment *other)
+{
+ Q_ASSERT(m_currentFrame->size() == other->m_currentFrame->size());
+
+ auto gb = graphBuilder();
+ Node *mergedControl = gb->mergeControl(controlDependency(), other->controlDependency());
+ setControlDependency(mergedControl);
+ setEffectDependency(gb->mergeEffect(effectDependency(), other->effectDependency(), mergedControl));
+
+ // insert/update phi nodes, but not for the unwind handler:
+ for (int i = 0, ei = m_currentFrame->size() - 1; i != ei; ++i) {
+ //### use lifeness info to trim this!
+ m_currentFrame->at(i) = gb->mergeValue(m_currentFrame->at(i),
+ other->m_currentFrame->at(i),
+ mergedControl);
+ }
+ Q_ASSERT(unwindHandlerOffset() >= 0); // specifically: don't crash
+ return mergedControl;
+}
+
+void GraphBuilder::buildGraph(IR::Function *function)
+{
+ const char *code = function->v4Function()->codeData;
+ uint len = function->v4Function()->compiledFunction->codeSize;
+
+ GraphBuilder builder(function);
+ builder.startGraph();
+
+ InterpreterEnvironment initial(&builder, function->graph()->startNode());
+ initial.setupStartEnvironment();
+ builder.setEnv(&initial);
+ builder.graph()->setInitialFrameState(initial.createFrameState());
+ builder.decode(code, len);
+ builder.endGraph();
+};
+
+GraphBuilder::GraphBuilder(IR::Function *function)
+ : m_func(function)
+ , m_graph(function->graph())
+ , m_currentEnv(nullptr)
+{
+ for (unsigned i = 0, ei = m_func->v4Function()->compiledFunction->nLabelInfos; i != ei; ++i) {
+ unsigned label = m_func->v4Function()->compiledFunction->labelInfoTable()[i];
+ m_labelInfos.emplace_back(label);
+ if (lcIRGraphBuilder().isDebugEnabled()) {
+ const LabelInfo &li = m_labelInfos.back();
+ qCDebug(lcIRGraphBuilder) << "Loop start at" << li.labelOffset;
+ }
+ }
+}
+
+void GraphBuilder::startGraph()
+{
+ size_t nValuesOut = 1 + CallData::HeaderSize()
+ + m_func->v4Function()->compiledFunction->nFormals;
+ Node *start = m_graph->createNode(opBuilder()->getStart(uint16_t(nValuesOut)), nullptr, 0);
+ m_func->nodeInfo(start)->setBytecodeOffsets(0, 0);
+ m_graph->setStartNode(start);
+ m_graph->setEngineNode(m_graph->createNode(opBuilder()->get<Meta::Engine>(), &start, 1));
+ auto frame = m_graph->createNode(opBuilder()->get<Meta::CppFrame>(), &start, 1);
+ m_graph->setCppFrameNode(frame);
+ m_graph->setFunctionNode(m_graph->createNode(opBuilder()->get<Meta::Function>(),
+ &frame, 1));
+}
+
+void GraphBuilder::endGraph()
+{
+ const auto inputCount = uint16_t(m_exitControls.size());
+ Node **inputs = &m_exitControls.front();
+ Q_ASSERT(m_graph->endNode() == nullptr);
+ m_graph->setEndNode(m_graph->createNode(opBuilder()->getEnd(inputCount), inputs, inputCount));
+}
+
+Node *GraphBuilder::bindAcc(Node *n)
+{
+ return env()->bindAcc(n);
+}
+
+/* IMPORTANT!!!
+ *
+ * This might change the success environment, so don't call:
+ * env()->bindAcc(createNode(...))
+ * because the binding should only happen on success, but the call to env() will get the
+ * environment from *before* the new success environment was created. Instead, do:
+ * bindAcc(createNode(....))
+ */
+Node *GraphBuilder::createAndLinkNode(Operation *op, Node *operands[], size_t opCount,
+ bool incomplete)
+{
+ Q_ASSERT(op->effectInputCount() < 2);
+ Q_ASSERT(op->controlInputCount() < 2);
+
+ QVarLengthArray<Node *, 32> inputs(static_cast<int>(opCount));
+ std::copy_n(operands, opCount, inputs.data());
+
+ if (op->effectInputCount() == 1)
+ inputs.append(env()->effectDependency());
+ if (op->controlInputCount() == 1)
+ inputs.append(env()->controlDependency());
+ if (op->hasFrameStateInput())
+ inputs.append(env()->createFrameState());
+
+ Node *node = m_graph->createNode(op, inputs.data(), inputs.size(), incomplete);
+
+ if (op->needsBytecodeOffsets()) {
+ m_func->nodeInfo(node)->setBytecodeOffsets(currentInstructionOffset(),
+ nextInstructionOffset());
+ }
+
+ if (op->effectOutputCount() > 0)
+ env()->setEffectDependency(node);
+ if (op->controlOutputCount() > 0)
+ env()->setControlDependency(node);
+
+ if (op->canThrow() && env()->unwindHandlerOffset()) {
+ InterpreterSubEnvironment successEnv(this);
+ Node *control = env()->controlDependency();
+ control = m_graph->createNode(opBuilder()->get<Meta::OnException>(), &control, 1);
+ env()->setControlDependency(control);
+ auto unwindHandlerOffset = env()->unwindHandlerOffset();
+ mergeIntoSuccessor(unwindHandlerOffset);
+ }
+
+ return node;
+}
+
+Node *GraphBuilder::createNode(Operation *op, bool incomplete)
+{
+ return createAndLinkNode(op, nullptr, 0, incomplete);
+}
+
+Node *GraphBuilder::createNode(Operation *op, Node *n1)
+{
+ Node *buf[] = { n1 };
+ return createAndLinkNode(op, buf, arraySize(buf));
+}
+
+Node *GraphBuilder::createNode(Operation *op, Node *n1, Node *n2)
+{
+ Node *buf[] = { n1, n2 };
+ return createAndLinkNode(op, buf, arraySize(buf));
+}
+
+Node *GraphBuilder::createNode(Operation *op, Node *n1, Node *n2, Node *n3)
+{
+ Node *buf[] = { n1, n2, n3 };
+ return createAndLinkNode(op, buf, arraySize(buf));
+}
+
+Node *GraphBuilder::createNode(Operation *op, Node *n1, Node *n2, Node *n3, Node *n4)
+{
+ Node *buf[] = { n1, n2, n3, n4 };
+ return createAndLinkNode(op, buf, arraySize(buf));
+}
+
+Node *GraphBuilder::createRegion(unsigned nControlInputs)
+{
+ return createNode(opBuilder()->getRegion(nControlInputs), true);
+}
+
+Node *GraphBuilder::createIfTrue()
+{
+ return createNode(opBuilder()->get<Meta::IfTrue>());
+}
+
+Node *GraphBuilder::createIfFalse()
+{
+ return createNode(opBuilder()->get<Meta::IfFalse>());
+}
+
+Node *GraphBuilder::createConstant(int v)
+{
+ return m_graph->createNode(opBuilder()->getConstant(Primitive::fromInt32(v)));
+}
+
+Node *GraphBuilder::createPhi(unsigned nInputs, Node *input, Node *control)
+{
+ auto phiOp = opBuilder()->getPhi(nInputs);
+ QVarLengthArray<Node *, 32> buffer(int(nInputs + 1));
+ std::fill_n(buffer.data(), nInputs, input);
+ buffer[int(nInputs)] = control;
+ return m_graph->createNode(phiOp, buffer.data(), nInputs + 1, true);
+}
+
+Node *GraphBuilder::createEffectPhi(unsigned nInputs, Node *input, Node *control)
+{
+ auto phiOp = opBuilder()->getEffectPhi(nInputs);
+ QVarLengthArray<Node *, 32> buffer(int(nInputs + 1));
+ std::fill_n(buffer.data(), nInputs, input);
+ buffer[int(nInputs)] = control;
+ return m_graph->createNode(phiOp, buffer.data(), nInputs + 1, true);
+}
+
+Node *GraphBuilder::createHandleUnwind(int offset)
+{
+ return createNode(opBuilder()->getHandleUnwind(offset));
+}
+
+Node *GraphBuilder::mergeControl(Node *c1, Node *c2)
+{
+ if (c1->operation()->kind() == Meta::Region) {
+ const unsigned nInputs = c1->operation()->controlInputCount() + 1;
+ c1->addInput(m_graph->pool(), c2);
+ c1->setOperation(opBuilder()->getRegion(nInputs));
+ return c1;
+ }
+ auto op = opBuilder()->getRegion(2);
+ Node *inputs[] = { c1, c2 };
+ return m_graph->createNode(op, inputs, 2);
+}
+
+Node *GraphBuilder::mergeEffect(Node *e1, Node *e2, Node *control)
+{
+ const unsigned nInputs = control->operation()->controlInputCount();
+ if (e1->operation()->kind() == Meta::EffectPhi && e1->controlInput() == control) {
+ e1->insertInput(m_graph->pool(), nInputs - 1, e2);
+ e1->setOperation(opBuilder()->getEffectPhi(nInputs));
+ return e1;
+ }
+
+ if (e1 != e2) {
+ Node *phi = createEffectPhi(nInputs, e1, control);
+ phi->replaceInput(nInputs - 1, e2);
+ return phi;
+ }
+
+ return e1;
+}
+
+Node *GraphBuilder::mergeValue(Node *v1, Node *v2, Node *control)
+{
+ const unsigned nInputs = control->operation()->controlInputCount();
+ if (v1->operation()->kind() == Meta::Phi && v1->controlInput() == control) {
+ v1->insertInput(m_graph->pool(), nInputs - 1, v2);
+ v1->setOperation(opBuilder()->getPhi(nInputs));
+ return v1;
+ }
+
+ if (v1 != v2) {
+ Node *phi = createPhi(nInputs, v1, control);
+ phi->replaceInput(nInputs - 1, v2);
+ return phi;
+ }
+
+ return v1;
+}
+
+Node *GraphBuilder::createToBoolean(Node *input)
+{
+ return createNode(opBuilder()->get<Meta::ToBoolean>(), input);
+}
+
+void GraphBuilder::populate(VarArgNodes &args, int argc, int argv)
+{
+ for (int i = 0; i < argc; ++i)
+ args.append(env()->slot(argv + i));
+ Q_ASSERT(argc >= 0 && argc <= std::numeric_limits<uint16_t>::max());
+}
+
+void GraphBuilder::queueFunctionExit(Node *exitNode)
+{
+ m_exitControls.push_back(exitNode);
+ setEnv(nullptr);
+}
+
+Node *GraphBuilder::mergeIntoSuccessor(int offset)
+{
+ InterpreterEnvironment *&successorEnvironment = m_envForOffset[offset];
+
+ Node *region = nullptr;
+ if (successorEnvironment == nullptr) {
+ region = createRegion(1);
+ successorEnvironment = env();
+ } else {
+ // Merge any values which are live coming into the successor.
+ region = successorEnvironment->merge(env());
+ }
+ setEnv(nullptr);
+ return region;
+}
+
+const GraphBuilder::LabelInfo *GraphBuilder::labelInfoAt(unsigned offset) const
+{
+ for (const LabelInfo &li : m_labelInfos) {
+ if (li.labelOffset == offset)
+ return &li;
+ }
+ return nullptr;
+}
+
+const GraphBuilder::LabelInfo *GraphBuilder::isLoopStart(unsigned offset) const
+{
+ if (auto li = labelInfoAt(offset)) {
+ //### in the future, check if this is a loop start, or some other label
+ return li;
+ }
+
+ return nullptr;
+}
+
+void GraphBuilder::handleLoopStart(const LabelInfo &labelInfo)
+{
+ Q_ASSERT(env() != nullptr);
+
+ // We unconditionally insert a region node with phi nodes here. Now there might already be
+ // such a node, (e.g. the region after an if-then-else), but for simplicity we ignore that.
+ // A subsequent pass will fold/remove chains of Region nodes.
+ //### FIXME: add a DCE pass
+
+ const auto offset = int(labelInfo.labelOffset);
+ Node *control = createRegion(1);
+ env()->setControlDependency(control);
+ Node *effect = createEffectPhi(1, env()->effectDependency(), control);
+ env()->setEffectDependency(effect);
+
+ // insert/update phi nodes, but not for the unwind handler:
+ for (int i = 0, ei = env()->slotCount() - 1; i != ei; ++i) {
+ //### use lifeness info to trim this further!
+ if (i == CallData::Accumulator)
+ continue; // should never be alive on loop entry
+ env()->bindNodeToSlot(createPhi(1, env()->slot(i), control), i);
+ }
+
+ m_envForOffset.insert(offset, env()->copy());
+}
+
+void GraphBuilder::startUnwinding()
+{
+ if (int target = env()->unwindHandlerOffset()) {
+ mergeIntoSuccessor(target);
+ } else {
+ bindAcc(graph()->undefinedNode());
+ generate_Ret();
+ }
+}
+
+void GraphBuilder::generate_Ret()
+{
+ Node* control = createNode(opBuilder()->get<Meta::Return>(), env()->accumulator());
+ queueFunctionExit(control);
+}
+
+void GraphBuilder::generate_Debug() { Q_UNREACHABLE(); }
+
+void GraphBuilder::generate_LoadConst(int index)
+{
+ auto func = function()->v4Function();
+ Value v = func->compilationUnit->constants[index];
+ bindAcc(createNode(opBuilder()->getConstant(v)));
+}
+
+void GraphBuilder::generate_LoadZero()
+{
+ bindAcc(createConstant(0));
+}
+
+void GraphBuilder::generate_LoadTrue()
+{
+ bindAcc(m_graph->trueConstant());
+}
+
+void GraphBuilder::generate_LoadFalse()
+{
+ bindAcc(m_graph->falseConstant());
+}
+
+void GraphBuilder::generate_LoadNull()
+{
+ bindAcc(m_graph->nullNode());
+}
+
+void GraphBuilder::generate_LoadUndefined()
+{
+ bindAcc(m_graph->undefinedNode());
+}
+
+void GraphBuilder::generate_LoadInt(int value)
+{
+ bindAcc(m_graph->createNode(opBuilder()->getConstant(Primitive::fromInt32(value))));
+}
+
+void GraphBuilder::generate_MoveConst(int constIndex, int destTemp)
+{
+ auto func = function()->v4Function();
+ Value v = func->compilationUnit->constants[constIndex];
+ env()->bindNodeToSlot(createNode(opBuilder()->getConstant(v)), destTemp);
+}
+
+void GraphBuilder::generate_LoadReg(int reg)
+{
+ bindAcc(env()->slot(reg));
+}
+
+void GraphBuilder::generate_StoreReg(int reg)
+{
+ Node *n = env()->accumulator();
+ if (reg == CallData::This)
+ n = createNode(opBuilder()->get<Meta::StoreThis>(), n);
+ env()->bindNodeToSlot(n, reg);
+}
+
+void GraphBuilder::generate_MoveReg(int srcReg, int destReg)
+{
+ env()->bindNodeToSlot(env()->slot(srcReg), destReg);
+}
+
+void GraphBuilder::generate_LoadImport(int index)
+{
+ auto func = function()->v4Function();
+ Value v = *func->compilationUnit->imports[index];
+ bindAcc(createNode(opBuilder()->getConstant(v)));
+}
+
+void GraphBuilder::generate_LoadLocal(int index, int /*traceSlot*/)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::ScopedLoad>(),
+ createConstant(0),
+ createConstant(index)));
+}
+
+void GraphBuilder::generate_StoreLocal(int index)
+{
+ createNode(opBuilder()->get<Meta::ScopedStore>(),
+ createConstant(0),
+ createConstant(index),
+ env()->accumulator());
+}
+
+void GraphBuilder::generate_LoadScopedLocal(int scope, int index, int /*traceSlot*/)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::ScopedLoad>(),
+ createConstant(scope),
+ createConstant(index)));
+}
+
+void GraphBuilder::generate_StoreScopedLocal(int scope, int index)
+{
+ createNode(opBuilder()->get<Meta::ScopedStore>(),
+ createConstant(scope),
+ createConstant(index),
+ env()->accumulator());
+}
+
+void GraphBuilder::generate_LoadRuntimeString(int stringId)
+{
+ auto func = function()->v4Function();
+ Value v = Value::fromHeapObject(func->compilationUnit->runtimeStrings[stringId]);
+ bindAcc(createNode(opBuilder()->getConstant(v)));
+}
+
+void GraphBuilder::generate_MoveRegExp(int regExpId, int destReg)
+{
+ env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::LoadRegExp>(),
+ createConstant(regExpId)), destReg);
+}
+
+void GraphBuilder::generate_LoadClosure(int value)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSLoadClosure>(),
+ createConstant(value)));
+}
+
+void GraphBuilder::generate_LoadName(int name, int /*traceSlot*/)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSLoadName>(),
+ createConstant(name)));
+}
+
+void GraphBuilder::generate_LoadGlobalLookup(int index, int /*traceSlot*/)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSLoadGlobalLookup>(), createConstant(index)));
+}
+
+void GraphBuilder::generate_StoreNameSloppy(int name)
+{
+ createNode(opBuilder()->get<Meta::JSStoreNameSloppy>(), createConstant(name), env()->accumulator());
+}
+
+void GraphBuilder::generate_StoreNameStrict(int name)
+{
+ createNode(opBuilder()->get<Meta::JSStoreNameStrict>(), createConstant(name), env()->accumulator());
+}
+
+void GraphBuilder::generate_LoadElement(int base, int /*traceSlot*/)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSLoadElement>(),
+ env()->slot(base),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_StoreElement(int base, int index, int /*traceSlot*/)
+{
+ createNode(opBuilder()->get<Meta::JSStoreElement>(),
+ env()->slot(base),
+ env()->slot(index),
+ env()->accumulator());
+}
+
+void GraphBuilder::generate_LoadProperty(int name, int /*traceSlot*/)
+{
+ Node *n = createNode(opBuilder()->get<Meta::JSLoadProperty>(),
+ env()->accumulator(),
+ createConstant(name));
+ bindAcc(n);
+}
+
+void GraphBuilder::generate_GetLookup(int index, int /*traceSlot*/)
+{
+ Node *n = createNode(opBuilder()->get<Meta::JSGetLookup>(),
+ env()->accumulator(),
+ createConstant(index));
+ bindAcc(n);
+}
+
+void GraphBuilder::generate_StoreProperty(int name, int base)
+{
+ createNode(opBuilder()->get<Meta::JSStoreProperty>(),
+ env()->slot(base),
+ createConstant(name),
+ env()->accumulator());
+}
+
+void GraphBuilder::generate_SetLookup(int index, int base)
+{
+
+ function()->v4Function()->isStrict()
+ ? createNode(opBuilder()->get<Meta::JSSetLookupStrict>(), env()->slot(base),
+ createConstant(index), env()->accumulator())
+ : createNode(opBuilder()->get<Meta::JSSetLookupSloppy>(), env()->slot(base),
+ createConstant(index), env()->accumulator());
+}
+
+void GraphBuilder::generate_LoadSuperProperty(int property)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSLoadSuperProperty>(),
+ env()->slot(property)));
+}
+
+void GraphBuilder::generate_StoreSuperProperty(int property)
+{
+ createNode(opBuilder()->get<Meta::JSStoreSuperProperty>(),
+ createConstant(property),
+ env()->accumulator());
+}
+
+void GraphBuilder::generate_LoadQmlContextPropertyLookup(int propertyIndex, int /*traceSlot*/)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::QMLLoadQmlContextPropertyLookup>(),
+ createConstant(propertyIndex)));
+}
+
+void GraphBuilder::generate_Yield() { Q_UNREACHABLE(); }
+void GraphBuilder::generate_YieldStar() { Q_UNREACHABLE(); }
+void GraphBuilder::generate_Resume(int /*offset*/) { Q_UNREACHABLE(); }
+
+void GraphBuilder::finalizeCall(Operation::Kind kind, VarArgNodes &args, int argc, int argv)
+{
+ populate(args, argc, argv);
+ bindAcc(createAndLinkNode(opBuilder()->getJSVarArgsCall(kind, uint16_t(args.size())),
+ args.data(), size_t(args.size())));
+}
+
+void GraphBuilder::generate_CallValue(int name, int argc, int argv, int /*traceSlot*/)
+{
+ VarArgNodes args;
+ args.append(env()->slot(name));
+ finalizeCall(Meta::JSCallValue, args, argc, argv);
+}
+
+void GraphBuilder::generate_CallWithReceiver(int name, int thisObject, int argc, int argv,
+ int /*traceSlot*/)
+{
+ VarArgNodes args;
+ args.append(env()->slot(name));
+ args.append(env()->slot(thisObject));
+ finalizeCall(Meta::JSCallWithReceiver, args, argc, argv);
+}
+
+void GraphBuilder::generate_CallProperty(int name, int base, int argc, int argv, int /*traceSlot*/)
+{
+ VarArgNodes args;
+ args.append(env()->slot(base));
+ args.append(createConstant(name));
+ finalizeCall(Meta::JSCallProperty, args, argc, argv);
+}
+
+void GraphBuilder::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv,
+ int /*traceSlot*/)
+{
+ VarArgNodes args;
+ args.append(env()->slot(base));
+ args.append(createConstant(lookupIndex));
+ finalizeCall(Meta::JSCallLookup, args, argc, argv);
+}
+
+void GraphBuilder::generate_CallElement(int base, int index, int argc, int argv, int /*traceSlot*/)
+{
+ VarArgNodes args;
+ args.append(env()->slot(base));
+ args.append(env()->slot(index));
+ finalizeCall(Meta::JSCallElement, args, argc, argv);
+}
+
+void GraphBuilder::generate_CallName(int name, int argc, int argv, int /*traceSlot*/)
+{
+ VarArgNodes args;
+ args.append(createConstant(name));
+ finalizeCall(Meta::JSCallName, args, argc, argv);
+}
+
+void GraphBuilder::generate_CallPossiblyDirectEval(int argc, int argv, int /*traceSlot*/)
+{
+ VarArgNodes args;
+ finalizeCall(Meta::JSCallPossiblyDirectEval, args, argc, argv);
+}
+
+void GraphBuilder::generate_CallGlobalLookup(int index, int argc, int argv, int /*traceSlot*/)
+{
+ VarArgNodes args;
+ args.append(createConstant(index));
+ finalizeCall(Meta::JSCallGlobalLookup, args, argc, argv);
+}
+
+void GraphBuilder::generate_CallQmlContextPropertyLookup(int index, int argc, int argv,
+ int /*traceSlot*/)
+{
+ VarArgNodes args;
+ args.append(createConstant(index));
+ finalizeCall(Meta::QMLCallQmlContextPropertyLookup, args, argc, argv);
+}
+
+void GraphBuilder::generate_SetUnwindHandler(int offset)
+{
+ m_currentUnwindHandlerOffset = offset ? absoluteOffset(offset) : 0;
+ env()->setUnwindHandlerOffset(m_currentUnwindHandlerOffset);
+}
+
+void GraphBuilder::generate_UnwindDispatch()
+{
+ auto e = createNode(opBuilder()->get<Meta::HasException>(), graph()->engineNode());
+ createNode(opBuilder()->get<Meta::Branch>(), e);
+ {
+ InterpreterSubEnvironment subEnvironment(this);
+ createIfTrue();
+ startUnwinding();
+ }
+
+ createIfFalse();
+
+ const auto unwindHandlerOffset = env()->unwindHandlerOffset();
+ const auto fallthroughSuccessor = nextInstructionOffset();
+ auto nContinuations = m_func->unwindLabelOffsets().size() + 1;
+ if (unwindHandlerOffset)
+ ++nContinuations;
+ Q_ASSERT(nContinuations <= std::numeric_limits<unsigned>::max());
+ createNode(opBuilder()->getUnwindDispatch(unsigned(nContinuations), unwindHandlerOffset,
+ fallthroughSuccessor));
+
+ {
+ InterpreterSubEnvironment fallthroughEnv(this);
+ mergeIntoSuccessor(fallthroughSuccessor);
+ }
+
+ if (unwindHandlerOffset) {
+ InterpreterSubEnvironment unwindHandlerEnv(this);
+ createHandleUnwind(unwindHandlerOffset);
+ mergeIntoSuccessor(unwindHandlerOffset);
+ }
+
+ for (int unwindLabelOffset : m_func->unwindLabelOffsets()) {
+ if (unwindLabelOffset <= currentInstructionOffset())
+ continue;
+ InterpreterSubEnvironment unwindLabelEnv(this);
+ createHandleUnwind(unwindLabelOffset);
+ mergeIntoSuccessor(unwindLabelOffset);
+ }
+
+ setEnv(nullptr);
+}
+
+void GraphBuilder::generate_UnwindToLabel(int level, int offset)
+{
+ //### For de-optimization, the relative offset probably also needs to be stored
+ int unwinder = absoluteOffset(offset);
+ createNode(opBuilder()->get<Meta::UnwindToLabel>(),
+ createConstant(level),
+ createConstant(unwinder));
+ m_func->addUnwindLabelOffset(unwinder);
+ startUnwinding();
+}
+
+void GraphBuilder::generate_DeadTemporalZoneCheck(int name)
+{
+ Node *check = createNode(opBuilder()->get<Meta::IsEmpty>(), env()->accumulator());
+ createNode(opBuilder()->get<Meta::Branch>(), check);
+
+ { //### it's probably better to handle this by de-optimizing
+ InterpreterSubEnvironment subEnvironment(this);
+ createIfTrue();
+ createNode(opBuilder()->get<Meta::ThrowReferenceError>(),
+ createConstant(name));
+ startUnwinding();
+ }
+
+ createIfFalse();
+}
+
+void GraphBuilder::generate_ThrowException()
+{
+ createNode(opBuilder()->get<Meta::Throw>(), env()->accumulator());
+ startUnwinding();
+}
+
+void GraphBuilder::generate_GetException()
+{
+ bindAcc(createNode(opBuilder()->get<Meta::GetException>()));
+}
+
+void GraphBuilder::generate_SetException()
+{
+ createNode(opBuilder()->get<Meta::SetException>(),
+ env()->accumulator());
+}
+
+void GraphBuilder::generate_CreateCallContext()
+{
+ createNode(opBuilder()->get<Meta::JSCreateCallContext>());
+}
+
+void GraphBuilder::generate_PushCatchContext(int index, int name)
+{
+ createNode(opBuilder()->get<Meta::JSCreateCatchContext>(),
+ createConstant(index),
+ createConstant(name));
+}
+
+void GraphBuilder::generate_PushWithContext()
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSCreateWithContext>(),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_PushBlockContext(int index)
+{
+ createNode(opBuilder()->get<Meta::JSCreateBlockContext>(),
+ createConstant(index));
+}
+
+void GraphBuilder::generate_CloneBlockContext()
+{
+ createNode(opBuilder()->get<Meta::JSCloneBlockContext>());
+}
+
+void GraphBuilder::generate_PushScriptContext(int index)
+{
+ createNode(opBuilder()->get<Meta::JSCreateScriptContext>(), createConstant(index));
+}
+
+void GraphBuilder::generate_PopScriptContext()
+{
+ createNode(opBuilder()->get<Meta::JSPopScriptContext>());
+}
+
+void GraphBuilder::generate_PopContext()
+{
+ createNode(opBuilder()->get<Meta::PopContext>());
+}
+
+void GraphBuilder::generate_GetIterator(int iterator)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSGetIterator>(),
+ env()->accumulator(),
+ createConstant(iterator)));
+}
+
+void GraphBuilder::generate_IteratorNextAndFriends_TrailingStuff(Node *iterationNode,
+ int resultSlot)
+{
+ // See generate_IteratorNext for why this method exists.
+
+ // check that no-one messed around with the operation and made it throwing
+ Q_ASSERT(iterationNode->operation()->controlOutputCount() == 1);
+
+ // check that it's in the effect chain, because HasException relies on that
+ Q_ASSERT(iterationNode->operation()->effectOutputCount() == 1);
+
+ env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::SelectOutput>(),
+ iterationNode,
+ createConstant(1),
+ graph()->undefinedNode()),
+ resultSlot);
+ // Note: the following will NOT set the accumulator, because it contains the return value of
+ // the runtime call!
+ Node *ehCheck = createNode(opBuilder()->get<Meta::HasException>(), graph()->engineNode());
+ createNode(opBuilder()->get<Meta::Branch>(), ehCheck);
+
+ { // EH path:
+ InterpreterSubEnvironment subEnvironment(this);
+ createIfTrue();
+ if (auto ehOffset = env()->unwindHandlerOffset()) {
+ // Ok, there is an exception handler, so go there:
+ mergeIntoSuccessor(ehOffset);
+ } else {
+ // No Exception Handler, so keep the exception set in the engine, and leave the function
+ // a.s.a.p.:
+ bindAcc(graph()->undefinedNode());
+ generate_Ret();
+ }
+ }
+
+ // Normal control flow:
+ createIfFalse();
+}
+
+void GraphBuilder::generate_IteratorNext(int value, int done)
+{
+ // The way we model exceptions in the graph is that a runtime function will either succeed and
+ // return a value, or it fails and throws an exception. If it throws, the return value is not
+ // used because the method did not complete normally, and therefore it might be tainted.
+ //
+ // This is a problem for (and only for) IteratorNext and IteratorNextForYieldStart.
+ //
+ // What would happen in the normal case, is that the return value (done) is not used/assigned
+ // when IteratorNext throws, because the exception handling path is chosen. However, the
+ // interpreter *does* assign it, and will only check for an exception *after* that assignment.
+ //
+ // So, in order to work around this odd-duck behavior, we mark the operation as NoThrow,
+ // override the runtime method and flag it to not throw, and insert extra exception check nodes
+ // after the SelectOutput that follows the IteratorNext(ForYieldStar).
+ //
+ // Also note that the IteratorNext and IteratorNextForYieldStar are the only operations that
+ // have an inout parameter, and thus require a SelectOutput node to retrieve this.
+
+ Node *n = createNode(opBuilder()->get<Meta::JSIteratorNext>(),
+ env()->accumulator(),
+ graph()->undefinedNode());
+ bindAcc(n);
+ env()->bindNodeToSlot(n, done);
+ generate_IteratorNextAndFriends_TrailingStuff(n, value);
+}
+
+void GraphBuilder::generate_IteratorNextForYieldStar(int iterator, int object)
+{
+ // Please, PLEASE read the comment in generate_IteratorNext.
+ Node *n = createNode(opBuilder()->get<Meta::JSIteratorNextForYieldStar>(),
+ env()->accumulator(),
+ env()->slot(iterator),
+ graph()->undefinedNode());
+ // Note: the following is a tiny bit different from what generate_IteratorNext does.
+ bindAcc(n);
+ generate_IteratorNextAndFriends_TrailingStuff(n, object);
+}
+
+void GraphBuilder::generate_IteratorClose(int done)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSIteratorClose>(),
+ env()->accumulator(),
+ env()->slot(done)));
+}
+
+void GraphBuilder::generate_DestructureRestElement()
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSDestructureRestElement>(),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_DeleteProperty(int base, int index)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSDeleteProperty>(),
+ env()->slot(base),
+ env()->slot(index)));
+}
+
+void GraphBuilder::generate_DeleteName(int name)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSDeleteName>(),
+ createConstant(name)));
+}
+
+void GraphBuilder::generate_TypeofName(int name)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSTypeofName>(),
+ createConstant(name)));
+}
+
+void GraphBuilder::generate_TypeofValue()
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSTypeofValue>(), env()->accumulator()));
+}
+
+void GraphBuilder::generate_DeclareVar(int varName, int isDeletable)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSDeclareVar>(),
+ createConstant(isDeletable),
+ createConstant(varName)));
+}
+
+void GraphBuilder::generate_DefineArray(int argc, int argv)
+{
+ VarArgNodes args;
+ finalizeCall(Meta::JSDefineArray, args, argc, argv);
+}
+
+void GraphBuilder::generate_DefineObjectLiteral(int internalClassId, int argc, int argv)
+{
+ VarArgNodes args;
+ args.append(createConstant(internalClassId));
+ finalizeCall(Meta::JSDefineObjectLiteral, args, argc, argv);
+}
+
+void GraphBuilder::generate_CreateClass(int classIndex, int heritage, int computedNames)
+{
+ int argc = 0;
+ int argv = computedNames;
+
+ const QV4::CompiledData::Class *cls = function()->v4Function()->compilationUnit->unitData()
+ ->classAt(classIndex);
+ const CompiledData::Method *methods = cls->methodTable();
+ for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
+ if (methods[i].name == std::numeric_limits<unsigned>::max())
+ ++argc;
+ }
+
+ VarArgNodes args;
+ args.append(createConstant(classIndex));
+ args.append(env()->slot(heritage));
+ finalizeCall(Meta::JSCreateClass, args, argc, argv);
+}
+
+void GraphBuilder::generate_CreateMappedArgumentsObject()
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSCreateMappedArgumentsObject>()));
+}
+
+void GraphBuilder::generate_CreateUnmappedArgumentsObject()
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSCreateUnmappedArgumentsObject>()));
+}
+
+void GraphBuilder::generate_CreateRestParameter(int argIndex)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSCreateRestParameter>(),
+ createConstant(argIndex)));
+}
+
+void GraphBuilder::generate_ConvertThisToObject()
+{
+ Node* control = createNode(opBuilder()->get<Meta::JSThisToObject>(),
+ env()->slot(CallData::This));
+ env()->bindNodeToSlot(control, CallData::This);
+}
+
+void GraphBuilder::generate_LoadSuperConstructor()
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSLoadSuperConstructor>(),
+ env()->slot(CallData::Function)));
+}
+
+void GraphBuilder::generate_ToObject()
+{
+ bindAcc(createNode(opBuilder()->get<Meta::ToObject>(),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_CallWithSpread(int func, int thisObject, int argc, int argv,
+ int /*traceSlot*/)
+{
+ VarArgNodes args;
+ args.append(env()->slot(func));
+ args.append(env()->slot(thisObject));
+ finalizeCall(Meta::JSCallWithSpread, args, argc, argv);
+}
+
+void GraphBuilder::generate_TailCall(int func, int thisObject, int argc, int argv)
+{
+ VarArgNodes args;
+ args.append(env()->slot(func));
+ args.append(env()->slot(thisObject));
+ populate(args, argc, argv);
+ Node *n = createAndLinkNode(opBuilder()->getJSTailCall(uint16_t(args.size())), args.data(),
+ size_t(args.size()));
+ queueFunctionExit(n);
+}
+
+void GraphBuilder::generate_Construct(int func, int argc, int argv)
+{
+ VarArgNodes args;
+ args.append(env()->slot(func));
+ args.append(env()->accumulator());
+ finalizeCall(Meta::JSConstruct, args, argc, argv);
+}
+
+void GraphBuilder::generate_ConstructWithSpread(int func, int argc, int argv)
+{
+ VarArgNodes args;
+ args.append(env()->slot(func));
+ args.append(env()->accumulator());
+ finalizeCall(Meta::JSConstructWithSpread, args, argc, argv);
+}
+
+void GraphBuilder::generate_Jump(int offset)
+{
+ auto jumpTarget = absoluteOffset(offset);
+ mergeIntoSuccessor(jumpTarget);
+}
+
+void GraphBuilder::generate_JumpTrue(int /*traceSlot*/, int offset)
+{
+ createNode(opBuilder()->get<Meta::Branch>(), createToBoolean(env()->accumulator()));
+
+ {
+ InterpreterSubEnvironment subEnvironment(this);
+ auto jumpTarget = absoluteOffset(offset);
+ createIfTrue();
+ mergeIntoSuccessor(jumpTarget);
+ }
+
+ createIfFalse();
+}
+
+void GraphBuilder::generate_JumpFalse(int traceSlot, int offset)
+{
+ generate_JumpFalse(env()->accumulator(), traceSlot, offset);
+}
+
+void GraphBuilder::generate_JumpFalse(Node *condition, int /*traceSlot*/, int offset)
+{
+ createNode(opBuilder()->get<Meta::Branch>(), createToBoolean(condition));
+
+ {
+ InterpreterSubEnvironment subEnvironment(this);
+ auto jumpTarget = absoluteOffset(offset);
+ createIfFalse();
+ mergeIntoSuccessor(jumpTarget);
+ }
+
+ createIfTrue();
+}
+
+void GraphBuilder::generate_JumpNoException(int offset)
+{
+ auto e = createNode(opBuilder()->get<Meta::HasException>(), graph()->engineNode());
+ createNode(opBuilder()->get<Meta::Branch>(), e);
+
+ {
+ InterpreterSubEnvironment subEnvironment(this);
+ auto jumpTarget = absoluteOffset(offset);
+ createIfFalse();
+ mergeIntoSuccessor(jumpTarget);
+ }
+
+ createIfTrue();
+}
+
+void GraphBuilder::generate_JumpNotUndefined(int offset)
+{
+ Node *condition = createNode(opBuilder()->get<Meta::JSStrictEqual>(),
+ env()->accumulator(),
+ graph()->undefinedNode());
+ generate_JumpFalse(condition, NoTraceSlot, offset);
+}
+
+void GraphBuilder::generate_CmpEqNull()
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSEqual>(),
+ env()->accumulator(),
+ graph()->nullNode()));
+}
+
+void GraphBuilder::generate_CmpNeNull()
+{
+ generate_CmpEqNull();
+ bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_CmpEqInt(int lhs)
+{
+ auto left = createConstant(lhs);
+ Node* control = createNode(opBuilder()->get<Meta::JSEqual>(),
+ left,
+ env()->accumulator());
+ bindAcc(control);
+}
+
+void GraphBuilder::generate_CmpNeInt(int lhs)
+{
+ generate_CmpEqInt(lhs);
+ bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_CmpEq(int lhs)
+{
+ Node* control = createNode(opBuilder()->get<Meta::JSEqual>(),
+ env()->slot(lhs),
+ env()->accumulator());
+ bindAcc(control);
+}
+
+void GraphBuilder::generate_CmpNe(int lhs)
+{
+ generate_CmpEq(lhs);
+ bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_CmpGt(int lhs)
+{
+ Node* control = createNode(opBuilder()->get<Meta::JSGreaterThan>(),
+ env()->slot(lhs),
+ env()->accumulator());
+ bindAcc(control);
+}
+
+void GraphBuilder::generate_CmpGe(int lhs)
+{
+ Node* control = createNode(opBuilder()->get<Meta::JSGreaterEqual>(),
+ env()->slot(lhs),
+ env()->accumulator());
+ bindAcc(control);
+}
+
+void GraphBuilder::generate_CmpLt(int lhs)
+{
+ Node* control = createNode(opBuilder()->get<Meta::JSLessThan>(),
+ env()->slot(lhs),
+ env()->accumulator());
+ bindAcc(control);
+}
+
+void GraphBuilder::generate_CmpLe(int lhs)
+{
+ Node* control = createNode(opBuilder()->get<Meta::JSLessEqual>(),
+ env()->slot(lhs),
+ env()->accumulator());
+ bindAcc(control);
+}
+
+void GraphBuilder::generate_CmpStrictEqual(int lhs)
+{
+ Node* control = createNode(opBuilder()->get<Meta::JSStrictEqual>(),
+ env()->slot(lhs),
+ env()->accumulator());
+ bindAcc(control);
+}
+
+void GraphBuilder::generate_CmpStrictNotEqual(int lhs)
+{
+ generate_CmpStrictEqual(lhs);
+ bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_CmpIn(int lhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSIn>(), env()->slot(lhs),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_CmpInstanceOf(int lhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSInstanceOf>(), env()->slot(lhs),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_UNot()
+{
+ bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(),
+ createToBoolean(env()->accumulator())));
+}
+
+void GraphBuilder::generate_UPlus(int /*traceSlot*/)
+{
+ Node* control = createNode(opBuilder()->get<Meta::JSToNumber>(),
+ env()->accumulator());
+ bindAcc(control);
+}
+
+void GraphBuilder::generate_UMinus(int /*traceSlot*/)
+{
+ Node* control = createNode(opBuilder()->get<Meta::JSNegate>(),
+ env()->accumulator());
+ bindAcc(control);
+}
+
+void GraphBuilder::generate_UCompl()
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSBitXor>(),
+ env()->accumulator(),
+ createConstant(-1)));
+}
+
+void GraphBuilder::generate_Increment(int /*traceSlot*/)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSAdd>(),
+ env()->accumulator(),
+ createConstant(1)));
+}
+
+
+void GraphBuilder::generate_Decrement(int /*traceSlot*/)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSSubtract>(),
+ env()->accumulator(),
+ createConstant(1)));
+}
+
+void GraphBuilder::generate_Add(int lhs, int /*traceSlot*/)
+{
+ Node* control = createNode(opBuilder()->get<Meta::JSAdd>(),
+ env()->slot(lhs),
+ env()->accumulator());
+ bindAcc(control);
+}
+
+void GraphBuilder::generate_BitAnd(int lhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSBitAnd>(),
+ env()->slot(lhs),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_BitOr(int lhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSBitOr>(),
+ env()->slot(lhs),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_BitXor(int lhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSBitXor>(),
+ env()->slot(lhs),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_UShr(int lhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSUnsignedShiftRight>(),
+ env()->slot(lhs),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_Shr(int lhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSShiftRight>(),
+ env()->slot(lhs),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_Shl(int lhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSShiftLeft>(),
+ env()->slot(lhs),
+ env()->accumulator()));
+}
+
+
+void GraphBuilder::generate_BitAndConst(int rhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSBitAnd>(),
+ env()->accumulator(),
+ createConstant(rhs)));
+}
+
+void GraphBuilder::generate_BitOrConst(int rhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSBitOr>(),
+ env()->accumulator(),
+ createConstant(rhs)));
+}
+
+void GraphBuilder::generate_BitXorConst(int rhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSBitXor>(),
+ env()->accumulator(),
+ createConstant(rhs)));
+}
+
+void GraphBuilder::generate_UShrConst(int rhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSUnsignedShiftRight>(),
+ env()->accumulator(),
+ createConstant(rhs)));
+}
+
+void GraphBuilder::generate_ShrConst(int rhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSShiftRight>(),
+ env()->accumulator(),
+ createConstant(rhs)));
+}
+
+void GraphBuilder::generate_ShlConst(int rhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSShiftLeft>(),
+ env()->accumulator(),
+ createConstant(rhs)));
+}
+
+void GraphBuilder::generate_Exp(int lhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSExponentiate>(),
+ env()->slot(lhs),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_Mul(int lhs, int /*traceSlot*/)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSMultiply>(),
+ env()->slot(lhs),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_Div(int lhs)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSDivide>(),
+ env()->slot(lhs),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_Mod(int lhs, int /*traceSlot*/)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSModulo>(),
+ env()->slot(lhs),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_Sub(int lhs, int /*traceSlot*/)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSSubtract>(),
+ env()->slot(lhs),
+ env()->accumulator()));
+}
+
+void GraphBuilder::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
+{
+ for (int reg = firstReg; reg < firstReg + count; ++reg)
+ env()->bindNodeToSlot(graph()->emptyNode(), reg);
+}
+
+void GraphBuilder::generate_ThrowOnNullOrUndefined()
+{
+ createNode(opBuilder()->get<Meta::JSThrowOnNullOrUndefined>(),
+ env()->accumulator());
+}
+
+void GraphBuilder::generate_GetTemplateObject(int index)
+{
+ bindAcc(createNode(opBuilder()->get<Meta::JSGetTemplateObject>(),
+ createConstant(index)));
+}
+
+GraphBuilder::Verdict GraphBuilder::startInstruction(Moth::Instr::Type /*instr*/)
+{
+ // This handles a couple of cases on how flow control can end up at this instruction.
+
+ const auto off = currentInstructionOffset();
+ if (auto newEnv = m_envForOffset[off]) {
+ // Ok, there was a jump from before to this point (which registered an environment), so we
+ // have two options:
+ if (env() != newEnv && env() != nullptr) {
+ // There is a current environment different from the environment active when we took the
+ // jump. This happens with e.g. an if-then-else:
+ //
+ // acc = condition
+ // JumpFalse else-block
+ // ... then block
+ // Jump end-if
+ // else-block:
+ // ... else block
+ // end-if:
+ // .. some instruction <--- we're here
+ //
+ // in that case we merge the after-else environment into the after-then environment:
+ newEnv->merge(env());
+ } else {
+ // There is not a current environment. This can happen with e.g. a loop:
+ // loop-start:
+ // acc = condition
+ // JumpFalse loop-end
+ // ... loop body
+ // Jump loop-start
+ // loop-end:
+ // .... some instruction <--- we're here
+ //
+ // The last jump of the loop will clear the environment, so at this point we only have
+ // the environment registered by the JumpFalse. This is the asy case: no merges, just
+ // take the registered environment unchanged.
+ }
+
+ // Leave the merged environment as-is, and continue with a copy. We cannot change the
+ // registered environment in case this point also happens to be a loop start.
+ setEnv(newEnv->copy());
+ }
+
+ if (env() == nullptr) {
+ // Ok, there is no environment, meaning nobody jumped to this instruction, and the previous
+ // instruction doesn't let control flow end up here. So, this is dead code.
+ // This can happen for JS like:
+ //
+ // if (condition) {
+ // return something
+ // } else {
+ // return somethingElse
+ // }
+ // someCode <--- we're here
+ return SkipInstruction;
+ }
+
+ const LabelInfo *info = isLoopStart(off);
+ if (info && env()) {
+ // Ok, this instruction is the start of a loop, meaning there will be a jump backwards to
+ // this point. Make sure there is a Region node with Phi nodes here.
+ handleLoopStart(*info);
+ }
+
+ return ProcessInstruction;
+}
+
+void GraphBuilder::endInstruction(Moth::Instr::Type /*instr*/) {}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4graphbuilder_p.h b/src/qml/jit/qv4graphbuilder_p.h
new file mode 100644
index 0000000000..450d8640b7
--- /dev/null
+++ b/src/qml/jit/qv4graphbuilder_p.h
@@ -0,0 +1,298 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4GRAPHBUILDER_P_H
+#define QV4GRAPHBUILDER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4global_p.h>
+#include <private/qv4bytecodehandler_p.h>
+#include <private/qv4ir_p.h>
+#include "qv4graph_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+// The graph builder walks the byte-code, and produces a graph. The graph is a digraph, where the
+// nodes have operations, and the edges are dependencies (or inputs).
+class GraphBuilder: protected Moth::ByteCodeHandler
+{
+ Q_DISABLE_COPY_MOVE(GraphBuilder)
+
+ enum { NoTraceSlot = -1 };
+
+ struct LabelInfo { //### extend this to also capture the amount of slots that are live
+ LabelInfo() = default;
+ LabelInfo(unsigned label) : labelOffset(label) {}
+ unsigned labelOffset = 0;
+ };
+
+public:
+ static void buildGraph(IR::Function *function);
+
+ class InterpreterEnvironment;
+
+ void setEnv(InterpreterEnvironment *newEnv)
+ { m_currentEnv = newEnv; }
+
+ InterpreterEnvironment *env() const
+ { return m_currentEnv; }
+
+private:
+ GraphBuilder(IR::Function *function);
+ ~GraphBuilder() override = default;
+
+ void startGraph();
+ void endGraph();
+
+ Node *bindAcc(Node *n);
+ Node *createAndLinkNode(Operation *op, Node *operands[], size_t opCount, bool incomplete = false);
+ Node *createNode(Operation *op, bool incomplete = false);
+ Node *createNode(Operation *op, Node *n1);
+ Node *createNode(Operation *op, Node *n1, Node *n2);
+ Node *createNode(Operation *op, Node *n1, Node *n2, Node *n3);
+ Node *createNode(Operation *op, Node *n1, Node *n2, Node *n3, Node *n4);
+ Node *createRegion(unsigned nControlInputs);
+ Node *createIfTrue();
+ Node *createIfFalse();
+ Node *createConstant(int v);
+ Node *createPhi(unsigned nInputs, Node *input, Node *control);
+ Node *createEffectPhi(unsigned nInputs, Node *input, Node *control);
+ Node *createHandleUnwind(int offset);
+ Node *mergeControl(Node *c1, Node *c2);
+ Node *mergeEffect(Node *e1, Node *e2, Node *control);
+ Node *mergeValue(Node *v1, Node *v2, Node *control);
+
+ Node *createToBoolean(Node *input);
+
+ using VarArgNodes = QVarLengthArray<Node *, 32>;
+ void populate(VarArgNodes &args, int argc, int argv);
+
+ void queueFunctionExit(Node *exitNode);
+
+ Function *function() const
+ { return m_func; }
+
+ Graph *graph()
+ { return m_graph; }
+
+ Node *mergeIntoSuccessor(int offset);
+
+ OperationBuilder *opBuilder() const
+ { return m_graph->opBuilder(); }
+
+ int absoluteOffset(int offset) const
+ { return offset + nextInstructionOffset(); }
+
+ const LabelInfo *labelInfoAt(unsigned offset) const;
+ const LabelInfo *isLoopStart(unsigned offset) const;
+ void handleLoopStart(const LabelInfo &labelInfo);
+ void startUnwinding();
+
+protected: // ByteCodeHandler
+ void generate_Ret() override;
+ void generate_Debug() override;
+ void generate_LoadConst(int index) override;
+ void generate_LoadZero() override;
+ void generate_LoadTrue() override;
+ void generate_LoadFalse() override;
+ void generate_LoadNull() override;
+ void generate_LoadUndefined() override;
+ void generate_LoadInt(int value) override;
+ void generate_MoveConst(int constIndex, int destTemp) override;
+ void generate_LoadReg(int reg) override;
+ void generate_StoreReg(int reg) override;
+ void generate_MoveReg(int srcReg, int destReg) override;
+ void generate_LoadImport(int index) override;
+ void generate_LoadLocal(int index, int traceSlot) override;
+ void generate_StoreLocal(int index) override;
+ void generate_LoadScopedLocal(int scope, int index, int traceSlot) override;
+ void generate_StoreScopedLocal(int scope, int index) override;
+ void generate_LoadRuntimeString(int stringId) override;
+ void generate_MoveRegExp(int regExpId, int destReg) override;
+ void generate_LoadClosure(int value) override;
+ void generate_LoadName(int name, int traceSlot) override;
+ void generate_LoadGlobalLookup(int index, int traceSlot) override;
+ void generate_StoreNameSloppy(int name) override;
+ void generate_StoreNameStrict(int name) override;
+ void generate_LoadElement(int base, int traceSlot) override;
+ void generate_StoreElement(int base, int index, int traceSlot) override;
+ void generate_LoadProperty(int name, int traceSlot) override;
+ void generate_GetLookup(int index, int traceSlot) override;
+ void generate_StoreProperty(int name, int base) override;
+ void generate_SetLookup(int index, int base) override;
+ void generate_LoadSuperProperty(int property) override;
+ void generate_StoreSuperProperty(int property) override;
+ void generate_LoadQmlContextPropertyLookup(int property, int traceSlot) override;
+ void generate_Yield() override;
+ void generate_YieldStar() override;
+ void generate_Resume(int offset) override;
+ void finalizeCall(Operation::Kind kind, VarArgNodes &args, int argc, int argv);
+ void generate_CallValue(int name, int argc, int argv, int traceSlot) override;
+ void generate_CallWithReceiver(int name, int thisObject, int argc, int argv,
+ int traceSlot) override;
+ void generate_CallProperty(int name, int base, int argc, int argv, int traceSlot) override;
+ void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv,
+ int traceSlot) override;
+ void generate_CallElement(int base, int index, int argc, int argv, int traceSlot) override;
+ void generate_CallName(int name, int argc, int argv, int traceSlot) override;
+ void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override;
+ void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override;
+ void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override;
+ void generate_SetUnwindHandler(int offset) override;
+ void generate_UnwindDispatch() override;
+ void generate_UnwindToLabel(int level, int offset) override;
+ void generate_DeadTemporalZoneCheck(int name) override;
+ void generate_ThrowException() override;
+ void generate_GetException() override;
+ void generate_SetException() override;
+ void generate_CreateCallContext() override;
+ void generate_PushCatchContext(int index, int name) override;
+ void generate_PushWithContext() override;
+ void generate_PushBlockContext(int index) override;
+ void generate_CloneBlockContext() override;
+ void generate_PushScriptContext(int index) override;
+ void generate_PopScriptContext() override;
+ void generate_PopContext() override;
+ void generate_GetIterator(int iterator) override;
+ void generate_IteratorNextAndFriends_TrailingStuff(Node *iterationNode, int resultSlot);
+ void generate_IteratorNext(int value, int done) override;
+ void generate_IteratorNextForYieldStar(int iterator, int object) override;
+ void generate_IteratorClose(int done) override;
+ void generate_DestructureRestElement() override;
+ void generate_DeleteProperty(int base, int index) override;
+ void generate_DeleteName(int name) override;
+ void generate_TypeofName(int name) override;
+ void generate_TypeofValue() override;
+ void generate_DeclareVar(int varName, int isDeletable) override;
+ void generate_DefineArray(int argc, int argv) override;
+ void generate_DefineObjectLiteral(int internalClassId, int argc, int argv) override;
+ void generate_CreateClass(int classIndex, int heritage, int computedNames) override;
+ void generate_CreateMappedArgumentsObject() override;
+ void generate_CreateUnmappedArgumentsObject() override;
+ void generate_CreateRestParameter(int argIndex) override;
+ void generate_ConvertThisToObject() override;
+ void generate_LoadSuperConstructor() override;
+ void generate_ToObject() override;
+ void generate_CallWithSpread(int func, int thisObject, int argc, int argv,
+ int traceSlot) override;
+ void generate_TailCall(int func, int thisObject, int argc, int argv) override;
+ void generate_Construct(int func, int argc, int argv) override;
+ void generate_ConstructWithSpread(int func, int argc, int argv) override;
+ void generate_Jump(int offset) override;
+ void generate_JumpTrue(int traceSlot, int offset) override;
+ void generate_JumpFalse(int traceSlot, int offset) override;
+ void generate_JumpFalse(Node *condition, int traceSlot, int offset);
+ void generate_JumpNoException(int offset) override;
+ void generate_JumpNotUndefined(int offset) override;
+ void generate_CmpEqNull() override;
+ void generate_CmpNeNull() override;
+ void generate_CmpEqInt(int lhs) override;
+ void generate_CmpNeInt(int lhs) override;
+ void generate_CmpEq(int lhs) override;
+ void generate_CmpNe(int lhs) override;
+ void generate_CmpGt(int lhs) override;
+ void generate_CmpGe(int lhs) override;
+ void generate_CmpLt(int lhs) override;
+ void generate_CmpLe(int lhs) override;
+ void generate_CmpStrictEqual(int lhs) override;
+ void generate_CmpStrictNotEqual(int lhs) override;
+ void generate_CmpIn(int lhs) override;
+ void generate_CmpInstanceOf(int lhs) override;
+ void generate_UNot() override;
+ void generate_UPlus(int traceSlot) override;
+ void generate_UMinus(int traceSlot) override;
+ void generate_UCompl() override;
+ void generate_Increment(int traceSlot) override;
+ void generate_Decrement(int traceSlot) override;
+ void generate_Add(int lhs, int traceSlot) override;
+ void generate_BitAnd(int lhs) override;
+ void generate_BitOr(int lhs) override;
+ void generate_BitXor(int lhs) override;
+ void generate_UShr(int lhs) override;
+ void generate_Shr(int lhs) override;
+ void generate_Shl(int lhs) override;
+ void generate_BitAndConst(int rhs) override;
+ void generate_BitOrConst(int rhs) override;
+ void generate_BitXorConst(int rhs) override;
+ void generate_UShrConst(int rhs) override;
+ void generate_ShrConst(int rhs) override;
+ void generate_ShlConst(int rhs) override;
+ void generate_Exp(int lhs) override;
+ void generate_Mul(int lhs, int traceSlot) override;
+ void generate_Div(int lhs) override;
+ void generate_Mod(int lhs, int traceSlot) override;
+ void generate_Sub(int lhs, int traceSlot) override;
+ void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override;
+ void generate_ThrowOnNullOrUndefined() override;
+ void generate_GetTemplateObject(int index) override;
+
+ Verdict startInstruction(Moth::Instr::Type instr) override;
+ void endInstruction(Moth::Instr::Type instr) override;
+
+private:
+ IR::Function *m_func;
+ Graph *m_graph;
+ InterpreterEnvironment *m_currentEnv;
+ std::vector<Node *> m_exitControls;
+ QHash<int, InterpreterEnvironment *> m_envForOffset;
+ std::vector<LabelInfo> m_labelInfos;
+ int m_currentUnwindHandlerOffset = 0;
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4GRAPHBUILDER_P_H
diff --git a/src/qml/jit/qv4ir.cpp b/src/qml/jit/qv4ir.cpp
new file mode 100644
index 0000000000..cb3eeeec60
--- /dev/null
+++ b/src/qml/jit/qv4ir.cpp
@@ -0,0 +1,382 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qqmlglobal_p.h>
+#include "qv4ir_p.h"
+#include "qv4node_p.h"
+#include "qv4function_p.h"
+#include <qv4graph_p.h>
+#include "qv4stackframe_p.h"
+#include "qv4operation_p.h"
+#include "qv4util_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qfile.h>
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcJsonIR, "qt.v4.ir.json");
+Q_LOGGING_CATEGORY(lcDotIR, "qt.v4.ir.dot");
+Q_LOGGING_CATEGORY(lcVerify, "qt.v4.ir.verify");
+
+Function::Function(QV4::Function *qv4Function)
+ : qv4Function(qv4Function)
+ , m_graph(Graph::create(this))
+ , m_dumper(nullptr)
+ , m_nodeInfo(128, nullptr)
+{
+}
+
+Function::~Function()
+{
+ delete m_dumper;
+}
+
+QString Function::name() const
+{
+ QString name;
+ if (auto n = v4Function()->name())
+ name = n->toQString();
+ if (name.isEmpty())
+ name = QString::asprintf("%p", v4Function());
+ auto loc = v4Function()->sourceLocation();
+ return name + QStringLiteral(" (%1:%2:%3)").arg(loc.sourceFile, QString::number(loc.line),
+ QString::number(loc.column));
+}
+
+void Function::dump(const QString &description) const
+{
+ Dumper::dump(this, description);
+}
+
+void Function::dump() const
+{
+ dump(QStringLiteral("Debug:"));
+}
+
+Dumper *Function::dumper() const
+{
+ if (!m_dumper)
+ m_dumper = new Dumper(this);
+ return m_dumper;
+}
+
+Function::StringId Function::addString(const QString &s)
+{
+ m_stringPool.push_back(s);
+ return m_stringPool.size() - 1;
+}
+
+NodeInfo *Function::nodeInfo(Node *n, bool createIfNecessary) const
+{
+ if (n->id() >= m_nodeInfo.size())
+ m_nodeInfo.resize(n->id() * 2, nullptr);
+
+ NodeInfo *&info = m_nodeInfo[n->id()];
+ if (info == nullptr && createIfNecessary) {
+ info = m_pool.New<NodeInfo>();
+ info->setType(n->operation()->type());
+ }
+ return info;
+}
+
+void Function::copyBytecodeOffsets(Node *from, Node *to)
+{
+ auto toInfo = nodeInfo(to);
+ if (auto fromInfo = nodeInfo(from)) {
+ toInfo->setBytecodeOffsets(fromInfo->currentInstructionOffset(),
+ fromInfo->nextInstructionOffset());
+ }
+}
+
+Dumper::Dumper(const Function *f)
+{
+ if (!f)
+ return;
+}
+
+void Dumper::dump(const Function *f, const QString &description)
+{
+ if (false && lcJsonIR().isDebugEnabled()) {
+ Dumper *dumper = f->dumper();
+
+ qCDebug(lcJsonIR).noquote().nospace() << description + QLatin1String(":\n");
+ for (const auto &line : dumper->dump(f).split('\n'))
+ qCDebug(lcJsonIR).noquote().nospace() << line;
+ }
+
+ if (lcDotIR().isDebugEnabled())
+ dot(f, description);
+}
+
+QByteArray Dumper::dump(const Function *f)
+{
+ QJsonObject fo;
+
+ {
+ QString name;
+ if (auto n = f->v4Function()->name())
+ name = n->toQString();
+ fo[QLatin1String("_searchKey")] = QStringLiteral("function %1").arg(name);
+ if (name.isEmpty())
+ name = QString::asprintf("%p", f->v4Function());
+ fo[QLatin1String("name")] = name;
+ }
+
+ auto loc = f->v4Function()->sourceLocation();
+ fo[QLatin1String("source")] = loc.sourceFile;
+ fo[QLatin1String("line")] = loc.line;
+ fo[QLatin1String("column")] = loc.column;
+
+ {
+ QJsonArray gn;
+ QJsonArray ge;
+ NodeCollector nodes(f->graph(), /*collectUses =*/ true);
+ nodes.sortById();
+ for (Node *n : nodes.reachable()) {
+ gn.append(dump(n, f));
+ int inputIndex = 0;
+ for (Node *input : n->inputs()) {
+ QJsonObject edge;
+ edge[QLatin1String("from")] = int(input->id());
+ edge[QLatin1String("to")] = int(n->id());
+ edge[QLatin1String("index")] = inputIndex;
+ if (inputIndex < n->operation()->valueInputCount()) {
+ edge[QLatin1String("type")] = QLatin1String("value");
+ } else if (inputIndex < n->operation()->valueInputCount()
+ + n->operation()->effectInputCount()) {
+ edge[QLatin1String("type")] = QLatin1String("effect");
+ } else {
+ edge[QLatin1String("type")] = QLatin1String("control");
+ }
+ Q_ASSERT(inputIndex < n->operation()->valueInputCount()
+ + n->operation()->effectInputCount()
+ + n->operation()->controlInputCount());
+ ge.append(edge);
+ ++inputIndex;
+ }
+ }
+ QJsonObject g;
+ g[QLatin1String("nodes")] = gn;
+ g[QLatin1String("edges")] = ge;
+ fo[QLatin1String("graph")] = g;
+ }
+
+ m_doc.setObject(fo);
+ return m_doc.toJson(QJsonDocument::Indented);
+}
+
+QJsonValue toJSonValue(QV4::Value v)
+{
+ switch (v.type()) {
+ case QV4::Value::Undefined_Type: return QJsonValue(QJsonValue::Undefined);
+ case QV4::Value::Null_Type: return QJsonValue(QJsonValue::Null);
+ case QV4::Value::Boolean_Type: return QJsonValue(v.booleanValue());
+ case QV4::Value::Integer_Type: return QJsonValue(v.int_32());
+ case QV4::Value::Managed_Type:
+ if (String *s = v.stringValue())
+ return QJsonValue(s->toQString());
+ else
+ return QJsonValue(QLatin1String("<managed>"));
+ default: return QJsonValue(v.doubleValue());
+ }
+}
+
+QJsonValue Dumper::dump(const Node * const node, const Function *f)
+{
+ QJsonObject n;
+ n[QLatin1String("id")] = int(node->id());
+ n[QLatin1String("kind")] = node->operation()->debugString();
+ switch (node->operation()->kind()) {
+ case Meta::Parameter: {
+ auto info = ParameterPayload::get(*node->operation());
+ n[QLatin1String("name")] = f->string(info->stringId());
+ n[QLatin1String("index")] = int(info->parameterIndex());
+ break;
+ }
+ case Meta::Constant: {
+ auto info = ConstantPayload::get(*node->operation());
+ n[QLatin1String("value")] = toJSonValue(info->value());
+ break;
+ }
+ default:
+ break;
+ }
+ return n;
+}
+
+void Dumper::dot(const Function *f, const QString &description)
+{
+ static const bool skipFramestate = qEnvironmentVariableIsSet("QV4_JIT_DOT_SKIP_FRAMESTATE");
+
+ auto node = [](Node *n) {
+ return QStringLiteral("n%1[label=\"%1: %2%3\"];\n").arg(QString::number(n->id()),
+ n->operation()->debugString(),
+ n->isDead() ? QStringLiteral(" (dead)")
+ : QString());
+ };
+
+ Graph *g = f->graph();
+ QString out;
+ out += QLatin1Char('\n');
+ out += QStringLiteral("digraph{root=\"n%1\" label=\"%2\";"
+ "node[shape=rect];"
+ "edge[dir=back fontsize=10];\n")
+ .arg(g->startNode()->id())
+ .arg(description);
+ out += node(g->startNode());
+ const bool dumpUses = false; // set to true to see all nodes
+ NodeCollector nodes(g, dumpUses, skipFramestate);
+ for (Node *n : nodes.reachable()) {
+ if (n == g->startNode())
+ continue;
+
+ out += node(n);
+
+ unsigned inputIndex = 0;
+ for (Node *input : n->inputs()) {
+ if (input == nullptr)
+ continue;
+ out += QStringLiteral("n%2->n%1[style=").arg(QString::number(n->id()),
+ QString::number(input->id()));
+ if (inputIndex < n->operation()->valueInputCount() ||
+ inputIndex == n->operation()->indexOfFrameStateInput()) {
+ out += QStringLiteral("solid headlabel=\"%1\"").arg(inputIndex);
+ } else if (inputIndex < unsigned(n->operation()->valueInputCount()
+ + n->operation()->effectInputCount())) {
+ out += QStringLiteral("dotted headlabel=\"%1\"").arg(inputIndex);
+ } else {
+ out += QStringLiteral("dashed headlabel=\"%1\"").arg(inputIndex);
+ }
+ out += QStringLiteral("];\n");
+ ++inputIndex;
+ }
+ }
+ out += QStringLiteral("}\n");
+ qCDebug(lcDotIR).nospace().noquote() << out;
+
+ QFile of(description + QStringLiteral(".dot"));
+ of.open(QIODevice::WriteOnly);
+ of.write(out.toUtf8());
+ of.close();
+}
+
+void Function::verify() const
+{
+#ifndef QT_NO_DEBUG
+ unsigned problemsFound = 0;
+
+ auto verifyNodeAgainstOperation = [&problemsFound](const Node *n) {
+ const Operation *op = n->operation();
+ if (op->totalInputCount() != n->inputCount()) {
+ ++problemsFound;
+ qCDebug(lcVerify()) << "Node" << n->id() << "has" << n->inputCount()
+ << "inputs, but it's operation" << op->debugString()
+ << "requires" << op->totalInputCount() << "inputs";
+ }
+
+ if (n->opcode() == Meta::Phi || n->opcode() == Meta::EffectPhi) {
+ if (n->controlInput()->opcode() != Meta::Region) {
+ ++problemsFound;
+ qCDebug(lcVerify()) << "Control input of phi node" << n->id() << "is not a region";
+ }
+ if (n->controlInput()->inputCount() + 1 != n->inputCount()) {
+ ++problemsFound;
+ qCDebug(lcVerify()) << "Control input of phi node" << n->id()
+ << "has" << n->controlInput()->inputCount()
+ << "inputs while phi node has" << n->inputCount()
+ << "inputs";
+ }
+ }
+
+ //### todo: verify outputs: value outputs are allowed to be unused, but the effect and
+ // control outputs have to be linked up, except:
+ //### todo: verify if no use is a nullptr, except for operations that can throw, where the
+ // last one is allowed to be a nullptr when an unwind handler is missing.
+ };
+
+ NodeWorkList todo(graph());
+ todo.enqueue(graph()->endNode());
+ while (Node *n = todo.dequeueNextNodeForVisiting()) {
+ todo.enqueueAllInputs(n);
+ todo.enqueueAllUses(n);
+
+ verifyNodeAgainstOperation(n);
+ }
+ //### TODO:
+ if (problemsFound != 0) {
+ dump(QStringLiteral("Problematic graph"));
+ qFatal("Found %u problems during graph verification!", problemsFound);
+ }
+#endif // QT_NO_xDEBUG
+}
+
+QString Type::debugString() const
+{
+ if (isNone())
+ return QStringLiteral("none");
+ if (isInvalid())
+ return QStringLiteral("invalid");
+
+ QStringList s;
+ if (m_t & Bool)
+ s += QStringLiteral("boolean");
+ if (m_t & Int32)
+ s += QStringLiteral("int32");
+ if (m_t & Double)
+ s += QStringLiteral("double");
+ if (m_t & Undefined)
+ s += QStringLiteral("undefined");
+ if (m_t & Null)
+ s += QStringLiteral("null");
+ if (m_t & Empty)
+ s += QStringLiteral("empty");
+ if (m_t & RawPointer)
+ s += QStringLiteral("raw pointer");
+
+ return s.join(QLatin1String(" "));
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4ir_p.h b/src/qml/jit/qv4ir_p.h
new file mode 100644
index 0000000000..e21a80528d
--- /dev/null
+++ b/src/qml/jit/qv4ir_p.h
@@ -0,0 +1,228 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4IR_P_H
+#define QV4IR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4function_p.h>
+#include <QtCore/qjsondocument.h>
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class Dumper;
+class Graph;
+
+class Node;
+class NodeInfo;
+
+class Function
+{
+ Q_DISABLE_COPY_MOVE(Function)
+public:
+ Function(QV4::Function *qv4Function);
+ ~Function();
+
+ void verify() const;
+
+ QV4::Function *v4Function() const
+ { return qv4Function; }
+
+ QString name() const;
+
+ QQmlJS::MemoryPool *pool()
+ { return &m_pool; }
+
+ Graph *graph() const
+ { return m_graph; }
+
+ void dump(const QString &description) const;
+ void dump() const; // for calling in the debugger
+ Dumper *dumper() const;
+
+ using StringId = size_t;
+ StringId addString(const QString &s);
+ QString string(StringId id) const
+ { return m_stringPool[id]; }
+
+ NodeInfo *nodeInfo(Node *n, bool createIfNecessary = true) const;
+ void copyBytecodeOffsets(Node *from, Node *to);
+
+ void addUnwindLabelOffset(int absoluteOffset)
+ { m_unwindLabelOffsets.push_back(absoluteOffset); }
+
+ const std::vector<int> &unwindLabelOffsets() const
+ { return m_unwindLabelOffsets; }
+
+private:
+ QV4::Function *qv4Function;
+ mutable QQmlJS::MemoryPool m_pool;
+ Graph *m_graph;
+ mutable Dumper *m_dumper;
+ std::vector<QString> m_stringPool;
+ mutable std::vector<NodeInfo *> m_nodeInfo; //### move the into the _pool
+ std::vector<int> m_unwindLabelOffsets;
+};
+
+class Dumper
+{
+ Q_DISABLE_COPY_MOVE(Dumper)
+
+public:
+ Dumper(const Function *f);
+ ~Dumper() = default;
+
+ static void dump(const Function *f, const QString &description);
+ static void dot(const Function *f, const QString &description);
+
+private:
+ QByteArray dump(const Function *f);
+ QJsonValue dump(const Node *node, const Function *f);
+
+private:
+ QJsonDocument m_doc;
+};
+
+class Type
+{
+ // None is for nodes with no type (e.g. a Return)
+ // The others form a lattice:
+ // Any -> Object -> Invalid
+ // ^^^ -> Number -> Integral -> Int32 -> ^^^^^^^
+ // ^^^ -> Number -> Integral -> UInt32 -> ^^^^^^^
+ // ^^^ -> Number -> Integral -> Bool -> ^^^^^^^
+ // ^^^ -> Number -> Double -> ^^^^^^^
+ // ^^^ -> Undefined -> ^^^^^^^
+ // ^^^ -> Null -> ^^^^^^^
+ // ^^^ -> Empty -> ^^^^^^^
+ enum InternalType: int16_t {
+ None = 0,
+
+ Object = 1 << 0,
+ Bool = 1 << 1,
+ Int32 = 1 << 2,
+ UInt32 = 1 << 3,
+ Double = 1 << 4,
+ Undefined = 1 << 5,
+ Null = 1 << 6,
+ Empty = 1 << 7,
+ RawPointer = 1 << 8,
+ Invalid = -1,
+
+ Integral = Int32 | UInt32 | Bool,
+ Number = Integral | Double,
+ Any = Object | Number | Undefined | Empty | Null,
+ };
+
+ Type(InternalType t) : m_t(t) {}
+
+public:
+ Type() = default;
+
+ bool operator==(const Type &other) const
+ { return m_t == other.m_t; }
+
+ static Type noneType() { return Type(None); }
+ static Type anyType() { return Type(Any); }
+ static Type undefinedType() { return Type(Undefined); }
+ static Type emptyType() { return Type(Empty); }
+ static Type booleanType() { return Type(Bool); }
+ static Type int32Type() { return Type(Int32); }
+ static Type doubleType() { return Type(Double); }
+ static Type numberType() { return Type(Number); }
+ static Type nullType() { return Type(Null); }
+ static Type objectType() { return Type(Object); }
+ static Type rawPointerType() { return Type(RawPointer); }
+
+ bool isAny() const { return m_t == Any; }
+ bool isBoolean() const { return m_t == Bool; }
+ bool isInt32() const { return m_t == Int32; }
+ bool isInvalid() const { return m_t == Invalid; }
+ bool isNone() const { return m_t == None; }
+ bool isDouble() const { return m_t == Double; }
+ bool isUndefined() const { return m_t == Undefined; }
+ bool isNull() const { return m_t == Null; }
+ bool isEmpty() const { return m_t == Empty; }
+ bool isObject() const { return m_t == Object; }
+ bool isRawPointer() const { return m_t == RawPointer; }
+ bool isIntegral() const { return matches(Integral); }
+ bool isNumber() const { return matches(Number); }
+
+ Type operator|(Type other) const
+ { return Type(InternalType(int16_t(m_t) | int16_t(other.m_t))); }
+
+ Type &operator|=(Type other)
+ {
+ m_t = (InternalType(int16_t(m_t) | int16_t(other.m_t)));
+ return *this;
+ }
+
+ QString debugString() const;
+
+private:
+ bool matches(InternalType it) const
+ {
+ return (m_t & ~it) == 0 && (m_t & it) != 0;
+ }
+
+private:
+ InternalType m_t = None;
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4IR_P_H
diff --git a/src/qml/jit/qv4jithelpers.cpp b/src/qml/jit/qv4jithelpers.cpp
deleted file mode 100644
index 674fd8c8c8..0000000000
--- a/src/qml/jit/qv4jithelpers.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4jithelpers_p.h"
-#include "qv4engine_p.h"
-#include "qv4function_p.h"
-#include "qv4value_p.h"
-#include "qv4object_p.h"
-#include "qv4functionobject_p.h"
-#include "qv4lookup_p.h"
-#include <QtCore/private/qnumeric_p.h>
-
-#ifdef V4_ENABLE_JIT
-
-QT_BEGIN_NAMESPACE
-namespace QV4 {
-namespace JIT {
-namespace Helpers {
-
-void convertThisToObject(ExecutionEngine *engine, Value *t)
-{
- if (!t->isObject()) {
- if (t->isNullOrUndefined()) {
- *t = engine->globalObject->asReturnedValue();
- } else {
- *t = t->toObject(engine)->asReturnedValue();
- }
- }
-}
-
-ReturnedValue loadGlobalLookup(Function *f, ExecutionEngine *engine, int index)
-{
- Lookup *l = f->compilationUnit->runtimeLookups + index;
- return l->globalGetter(l, engine);
-}
-
-ReturnedValue loadQmlContextPropertyLookup(Function *f, ExecutionEngine *engine, int index)
-{
- Lookup *l = f->compilationUnit->runtimeLookups + index;
- return l->qmlContextPropertyGetter(l, engine, nullptr);
-}
-
-ReturnedValue toObject(ExecutionEngine *engine, const Value &obj)
-{
- if (obj.isObject())
- return obj.asReturnedValue();
-
- return obj.toObject(engine)->asReturnedValue();
-}
-
-ReturnedValue exp(const Value &base, const Value &exp)
-{
- double b = base.toNumber();
- double e = exp.toNumber();
- if (qt_is_inf(e) && (b == 1 || b == -1))
- return Encode(qt_snan());
- return Encode(pow(b,e));
-}
-
-ReturnedValue getLookup(Function *f, ExecutionEngine *engine, const Value &base, int index)
-{
- Lookup *l = f->compilationUnit->runtimeLookups + index;
- return l->getter(l, engine, base);
-}
-
-void setLookupSloppy(Function *f, int index, Value &base, const Value &value)
-{
- ExecutionEngine *engine = f->internalClass->engine;
- QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
- l->setter(l, engine, base, value);
-}
-
-void setLookupStrict(Function *f, int index, Value &base, const Value &value)
-{
- ExecutionEngine *engine = f->internalClass->engine;
- QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
- if (!l->setter(l, engine, base, value))
- engine->throwTypeError();
-}
-
-
-void pushBlockContext(Value *stack, int index)
-{
- ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- stack[CallData::Context] = Runtime::method_createBlockContext(c, index);
-}
-
-void cloneBlockContext(Value *contextSlot)
-{
- *contextSlot = Runtime::method_cloneBlockContext(static_cast<QV4::ExecutionContext *>(contextSlot));
-}
-
-void pushScriptContext(Value *stack, ExecutionEngine *engine, int index)
-{
- stack[CallData::Context] = Runtime::method_createScriptContext(engine, index);
-}
-
-void popScriptContext(Value *stack, ExecutionEngine *engine)
-{
- stack[CallData::Context] = Runtime::method_popScriptContext(engine);
-}
-
-ReturnedValue deleteProperty(QV4::Function *function, const QV4::Value &base, const QV4::Value &index)
-{
- auto engine = function->internalClass->engine;
- if (!Runtime::method_deleteProperty(engine, base, index)) {
- if (function->isStrict())
- engine->throwTypeError();
- return Encode(false);
- } else {
- return Encode(true);
- }
-}
-
-ReturnedValue deleteName(Function *function, int name)
-{
- auto engine = function->internalClass->engine;
- if (!Runtime::method_deleteName(engine, name)) {
- if (function->isStrict())
- engine->throwTypeError();
- return Encode(false);
- } else {
- return Encode(true);
- }
-}
-
-void throwOnNullOrUndefined(ExecutionEngine *engine, const Value &v)
-{
- if (v.isNullOrUndefined())
- engine->throwTypeError();
-}
-
-} // Helpers namespace
-} // JIT namespace
-} // QV4 namespace
-QT_END_NAMESPACE
-
-#endif // V4_ENABLE_JIT
diff --git a/src/qml/jit/qv4loopinfo.cpp b/src/qml/jit/qv4loopinfo.cpp
new file mode 100644
index 0000000000..0366c49e30
--- /dev/null
+++ b/src/qml/jit/qv4loopinfo.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qloggingcategory.h>
+
+#include "qv4loopinfo_p.h"
+#include "qv4domtree_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcLoopinfo, "qt.v4.ir.loopinfo")
+
+void LoopInfo::detectLoops()
+{
+ blockInfos.resize(dt.function()->blockCount());
+
+ std::vector<MIBlock *> backedges;
+ backedges.reserve(4);
+
+ const auto order = dt.calculateDFNodeIterOrder();
+ for (MIBlock *bb : order) {
+ if (bb->isDeoptBlock())
+ continue;
+
+ backedges.clear();
+
+ for (MIBlock *pred : bb->inEdges()) {
+ if (bb == pred || dt.dominates(bb->index(), pred->index()))
+ backedges.push_back(pred);
+ }
+
+ if (!backedges.empty())
+ subLoop(bb, backedges);
+ }
+
+ collectLoopExits();
+
+ dump();
+}
+
+void LoopInfo::collectLoopExits()
+{
+ for (MIBlock::Index i = 0, ei = MIBlock::Index(blockInfos.size()); i != ei; ++i) {
+ BlockInfo &bi = blockInfos[i];
+ MIBlock *currentBlock = dt.function()->block(i);
+ if (bi.isLoopHeader) {
+ for (MIBlock *outEdge : currentBlock->outEdges()) {
+ if (outEdge != currentBlock && !inLoopOrSubLoop(outEdge, currentBlock))
+ bi.loopExits.push_back(outEdge);
+ }
+ }
+ if (MIBlock *containingLoop = bi.loopHeader) {
+ BlockInfo &loopInfo = blockInfos[containingLoop->index()];
+ for (MIBlock *outEdge : currentBlock->outEdges()) {
+ if (outEdge != containingLoop && !inLoopOrSubLoop(outEdge, containingLoop))
+ loopInfo.loopExits.push_back(outEdge);
+ }
+ }
+ }
+}
+
+bool LoopInfo::inLoopOrSubLoop(MIBlock *block, MIBlock *loopHeader) const
+{
+ const BlockInfo &bi = blockInfos[block->index()];
+ MIBlock *loopHeaderForBlock = bi.loopHeader;
+ if (loopHeaderForBlock == nullptr)
+ return false; // block is not in any loop
+
+ while (loopHeader) {
+ if (loopHeader == loopHeaderForBlock)
+ return true;
+ // look into the parent loop of loopHeader to see if block is contained there
+ loopHeader = blockInfos[loopHeader->index()].loopHeader;
+ }
+
+ return false;
+}
+
+void LoopInfo::subLoop(MIBlock *loopHead, const std::vector<MIBlock *> &backedges)
+{
+ blockInfos[loopHead->index()].isLoopHeader = true;
+
+ std::vector<MIBlock *> worklist;
+ worklist.reserve(backedges.size() + 8);
+ worklist.insert(worklist.end(), backedges.begin(), backedges.end());
+ while (!worklist.empty()) {
+ MIBlock *predIt = worklist.back();
+ worklist.pop_back();
+
+ MIBlock *subloop = blockInfos[predIt->index()].loopHeader;
+ if (subloop) {
+ // This is a discovered block. Find its outermost discovered loop.
+ while (MIBlock *parentLoop = blockInfos[subloop->index()].loopHeader)
+ subloop = parentLoop;
+
+ // If it is already discovered to be a subloop of this loop, continue.
+ if (subloop == loopHead)
+ continue;
+
+ // Yay, it's a subloop of this loop.
+ blockInfos[subloop->index()].loopHeader = loopHead;
+ predIt = subloop;
+
+ // Add all predecessors of the subloop header to the worklist, as long as
+ // those predecessors are not in the current subloop. It might be the case
+ // that they are in other loops, which we will then add as a subloop to the
+ // current loop.
+ for (MIBlock *predIn : predIt->inEdges())
+ if (blockInfos[predIn->index()].loopHeader != subloop)
+ worklist.push_back(predIn);
+ } else {
+ if (predIt == loopHead)
+ continue;
+
+ // This is an undiscovered block. Map it to the current loop.
+ blockInfos[predIt->index()].loopHeader = loopHead;
+
+ // Add all incoming edges to the worklist.
+ for (MIBlock *bb : predIt->inEdges())
+ worklist.push_back(bb);
+ }
+ }
+}
+
+void LoopInfo::dump() const
+{
+ if (!lcLoopinfo().isDebugEnabled())
+ return;
+
+ QString s = QStringLiteral("Loop information:\n");
+ for (size_t i = 0, ei = blockInfos.size(); i != ei; ++i) {
+ const BlockInfo &bi = blockInfos[i];
+ s += QStringLiteral(" %1 : is loop header: %2, contained in loop header's loop: ")
+ .arg(i).arg(bi.isLoopHeader ? QLatin1String("yes") : QLatin1String("no"));
+ if (bi.loopHeader)
+ s += QString::number(bi.loopHeader->index());
+ else
+ s += QLatin1String("<none>");
+ if (bi.isLoopHeader) {
+ s += QStringLiteral(", loop exits: ");
+ if (bi.loopExits.empty()) {
+ s += QLatin1String("<none>");
+ } else {
+ bool first = true;
+ for (MIBlock *exit : bi.loopExits) {
+ if (first)
+ first = false;
+ else
+ s += QStringLiteral(", ");
+ s += QString::number(exit->index());
+ }
+ }
+ }
+ s += QLatin1Char('\n');
+ }
+ qCDebug(lcLoopinfo).noquote().nospace() << s;
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4loopinfo_p.h b/src/qml/jit/qv4loopinfo_p.h
new file mode 100644
index 0000000000..6a865e6dc6
--- /dev/null
+++ b/src/qml/jit/qv4loopinfo_p.h
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4LOOPINFO_P_H
+#define QV4LOOPINFO_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4mi_p.h"
+
+#include <QHash>
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class DominatorTree;
+
+// Detect all (sub-)loops in a function.
+//
+// Doing loop detection on the CFG is better than relying on the statement information in
+// order to mark loops. Although JavaScript only has natural loops, it can still be the case
+// that something is not a loop even though a loop-like-statement is in the source. For
+// example:
+// while (true) {
+// if (i > 0)
+// break;
+// else
+// break;
+// }
+//
+// Algorithm:
+// - do a DFS on the dominator tree, where for each node:
+// - collect all back-edges
+// - if there are back-edges, the node is a loop-header for a new loop, so:
+// - walk the CFG is reverse-direction, and for every node:
+// - if the node already belongs to a loop, we've found a nested loop:
+// - get the loop-header for the (outermost) nested loop
+// - add that loop-header to the current loop
+// - continue by walking all incoming edges that do not yet belong to the current loop
+// - if the node does not belong to a loop yet, add it to the current loop, and
+// go on with all incoming edges
+//
+// Loop-header detection by checking for back-edges is very straight forward: a back-edge is
+// an incoming edge where the other node is dominated by the current node. Meaning: all
+// execution paths that reach that other node have to go through the current node, that other
+// node ends with a (conditional) jump back to the loop header.
+//
+// The exact order of the DFS on the dominator tree is not important. The only property has to
+// be that a node is only visited when all the nodes it dominates have been visited before.
+// The reason for the DFS is that for nested loops, the inner loop's loop-header is dominated
+// by the outer loop's header. So, by visiting depth-first, sub-loops are identified before
+// their containing loops, which makes nested-loop identification free. An added benefit is
+// that the nodes for those sub-loops are only processed once.
+//
+// Note: independent loops that share the same header are merged together. For example, in
+// the code snippet below, there are 2 back-edges into the loop-header, but only one single
+// loop will be detected.
+// while (a) {
+// if (b)
+// continue;
+// else
+// continue;
+// }
+class LoopInfo
+{
+ Q_DISABLE_COPY_MOVE(LoopInfo)
+
+ struct BlockInfo
+ {
+ MIBlock *loopHeader = nullptr;
+ bool isLoopHeader = false;
+ std::vector<MIBlock *> loopExits;
+ };
+
+public:
+ LoopInfo(const DominatorTree &dt)
+ : dt(dt)
+ {}
+
+ ~LoopInfo() = default;
+
+ void detectLoops();
+
+ MIBlock *loopHeaderFor(MIBlock *bodyBlock) const
+ { return blockInfos[bodyBlock->index()].loopHeader; }
+
+ bool isLoopHeader(MIBlock *block) const
+ { return blockInfos[block->index()].isLoopHeader; }
+
+ const std::vector<MIBlock *> loopExitsForLoop(MIBlock *loopHeader) const
+ { return blockInfos[loopHeader->index()].loopExits; }
+
+private:
+ void subLoop(MIBlock *loopHead, const std::vector<MIBlock *> &backedges);
+ void collectLoopExits();
+ bool inLoopOrSubLoop(MIBlock *block, MIBlock *loopHeader) const;
+
+ void dump() const;
+
+private:
+ const DominatorTree &dt;
+ std::vector<BlockInfo> blockInfos;
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4LOOPINFO_P_H
diff --git a/src/qml/jit/qv4lowering.cpp b/src/qml/jit/qv4lowering.cpp
new file mode 100644
index 0000000000..3b3711e7fa
--- /dev/null
+++ b/src/qml/jit/qv4lowering.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QLoggingCategory>
+
+#include "qv4lowering_p.h"
+#include "qv4graph_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcLower, "qt.v4.ir.lowering")
+
+GenericLowering::GenericLowering(Function &f)
+ : m_function(f)
+{}
+
+void GenericLowering::lower()
+{
+ NodeWorkList worklist(graph());
+ // The order doesn't really matter for generic lowering, as long as it's done in 1 pass, and
+ // have any clean-up done afterwards.
+ worklist.enqueueAllInputs(graph()->endNode());
+
+ while (Node *n = worklist.dequeueNextNodeForVisiting()) {
+ worklist.enqueueAllInputs(n);
+
+ if (!CallPayload::isRuntimeCall(n->opcode()))
+ continue;
+
+ if (CallPayload::isVarArgsCall(n->opcode()))
+ replaceWithVarArgsCall(n);
+ else
+ replaceWithCall(n);
+ }
+}
+
+void GenericLowering::replaceWithCall(Node *n)
+{
+ auto newOp = opBuilder()->getCall(n->opcode());
+
+ QVarLengthArray<Node *, 32> args;
+ if (CallPayload::takesEngineAsArg(n->opcode(), 0))
+ args.append(graph()->engineNode());
+ if (CallPayload::takesFunctionAsArg(n->opcode(), args.size()))
+ args.append(graph()->functionNode());
+ if (CallPayload::takesFrameAsArg(n->opcode(), args.size()))
+ args.append(graph()->cppFrameNode());
+ const int extraLeadingArguments = args.size();
+
+ for (unsigned arg = 0, earg = n->inputCount(); arg != earg; ++arg) {
+ Node *input = n->input(arg);
+ if (input->opcode() == Meta::FrameState)
+ continue;
+
+ if (arg >= n->operation()->valueInputCount()) {
+ // effect or control input
+ args.append(input);
+ continue;
+ }
+
+ if (CallPayload::needsStorageOnJSStack(n->opcode(), args.size(), input->operation(),
+ function().nodeInfo(input)->type()))
+ input = graph()->createNode(opBuilder()->get<Meta::Alloca>(), input);
+
+ args.append(input);
+ }
+
+ Node *newCall = graph()->createNode(newOp, args.data(), args.size());
+
+ qCDebug(lcLower) << "replacing node" << n->id() << n->operation()->debugString()
+ << "with node" << newCall->id() << newOp->debugString();
+ qCDebug(lcLower) << "... old node #inputs:" << n->inputCount();
+ qCDebug(lcLower) << "... old node #uses:" << n->useCount();
+
+ function().nodeInfo(newCall)->setType(CallPayload::returnType(n->opcode()));
+ n->replaceAllUsesWith(newCall);
+ n->kill();
+
+ qCDebug(lcLower) << "... new node #inputs:" << newCall->inputCount();
+ qCDebug(lcLower) << "... new node #uses:" << newCall->useCount();
+
+ for (Node *use : newCall->uses()) {
+ // fix-up indices for SelectOutput:
+ if (use->opcode() == Meta::SelectOutput) {
+ const int oldIndex = ConstantPayload::get(*use->input(1)->operation())->value().int_32();
+ const int newIndex = oldIndex + extraLeadingArguments;
+ use->replaceInput(1, graph()->createConstantIntNode(newIndex));
+ use->replaceInput(2, newCall->input(newIndex));
+ break;
+ }
+ }
+}
+
+void GenericLowering::replaceWithVarArgsCall(Node *n)
+{
+ const bool isTailCall = n->opcode() == Meta::JSTailCall;
+ Operation *newOp = isTailCall ? opBuilder()->getTailCall()
+ : opBuilder()->getCall(n->opcode());
+
+ //### optimize this for 0 and 1 argument: we don't need to create a VarArgs array for these cases
+
+ const unsigned varArgsStart = CallPayload::varArgsStart(n->opcode()) - 1; // subtract 1 because the runtime calls all take the engine argument as arg0, which isn't in the graph before lowering.
+ Node *vaAlloc = graph()->createNode(
+ opBuilder()->get<Meta::VAAlloc>(),
+ graph()->createConstantIntNode(n->operation()->valueInputCount() - varArgsStart),
+ n->effectInput());
+ QVarLengthArray<Node *, 32> vaSealIn;
+ vaSealIn.append(vaAlloc);
+ for (unsigned i = varArgsStart, ei = n->operation()->valueInputCount(); i != ei; ++i) {
+ vaSealIn.append(graph()->createNode(opBuilder()->get<Meta::VAStore>(), vaAlloc,
+ graph()->createConstantIntNode(vaSealIn.size() - 1),
+ n->input(i)));
+ }
+ vaSealIn.append(vaAlloc);
+ Node *vaSeal = graph()->createNode(opBuilder()->getVASeal(vaSealIn.size() - 2),
+ vaSealIn.data(),
+ vaSealIn.size());
+ QVarLengthArray<Node *, 8> callArgs;
+ if (isTailCall)
+ callArgs.append(graph()->cppFrameNode());
+ callArgs.append(graph()->engineNode());
+ for (unsigned i = 0; i != varArgsStart; ++i) {
+ Node *input = n->input(i);
+ if (CallPayload::needsStorageOnJSStack(n->opcode(), callArgs.size(), input->operation(),
+ function().nodeInfo(input)->type()))
+ input = graph()->createNode(opBuilder()->get<Meta::Alloca>(), input);
+ callArgs.append(input);
+ }
+ callArgs.append(vaSeal); // args
+ if (n->opcode() != Meta::JSCreateClass) // JSCreateClass is the odd duck
+ callArgs.append(graph()->createConstantIntNode(vaSealIn.size() - 2)); // argc
+ callArgs.append(vaSeal); // effect
+ callArgs.append(n->controlInput(0)); // control flow
+ Node *newCall = graph()->createNode(newOp, callArgs.data(), unsigned(callArgs.size()));
+
+ qCDebug(lcLower) << "replacing node" << n->id() << n->operation()->debugString()
+ << "with node" << newCall->id() << newOp->debugString();
+ qCDebug(lcLower) << "... old node #inputs:" << n->inputCount();
+ qCDebug(lcLower) << "... old node #uses:" << n->useCount();
+
+ n->replaceAllUsesWith(newCall);
+ n->kill();
+
+ qCDebug(lcLower) << "... new node #inputs:" << newCall->inputCount();
+ qCDebug(lcLower) << "... new node #uses:" << newCall->useCount();
+}
+
+bool GenericLowering::allUsesAsUnboxedBool(Node *n)
+{
+ for (Node *use : n->uses()) {
+ if (use->operation()->kind() != Meta::Branch)
+ return false;
+ }
+
+ return true;
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4jithelpers_p.h b/src/qml/jit/qv4lowering_p.h
index d9abfc071e..0b482bc9f0 100644
--- a/src/qml/jit/qv4jithelpers_p.h
+++ b/src/qml/jit/qv4lowering_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef TEMPLATE_H
-#define TEMPLATE_H
+#ifndef QV4LOWERING_P_H
+#define QV4LOWERING_P_H
//
// W A R N I N G
@@ -51,42 +51,57 @@
// We mean it.
//
+#include <private/qqmljsmemorypool_p.h>
#include <private/qv4global_p.h>
+#include <private/qv4ir_p.h>
+#include <private/qv4util_p.h>
+#include <private/qv4node_p.h>
+#include <private/qv4graph_p.h>
-//QT_REQUIRE_CONFIG(qml_jit);
+QT_REQUIRE_CONFIG(qml_tracing);
QT_BEGIN_NAMESPACE
namespace QV4 {
+namespace IR {
-#ifdef V4_ENABLE_JIT
+// Lowering replaces JS level operations with lower level ones. E.g. a JSAdd is lowered to an AddI32
+// if both inputs and the output are 32bit integers, or to a runtime call in all other cases. This
+// transforms the graph into something that is closer to actual executable code.
-namespace JIT {
-namespace Helpers {
-void convertThisToObject(ExecutionEngine *engine, Value *t);
-ReturnedValue loadGlobalLookup(Function *f, ExecutionEngine *engine, int index);
-ReturnedValue loadQmlContextPropertyLookup(Function *f, ExecutionEngine *engine, int index);
-ReturnedValue toObject(ExecutionEngine *engine, const Value &obj);
-ReturnedValue exp(const Value &base, const Value &exp);
-ReturnedValue getLookup(Function *f, ExecutionEngine *engine, const Value &base, int index);
-void setLookupStrict(Function *f, int index, Value &base, const Value &value);
-void setLookupSloppy(Function *f, int index, Value &base, const Value &value);
-void pushBlockContext(Value *stack, int index);
-void cloneBlockContext(Value *contextSlot);
-void pushScriptContext(Value *stack, ExecutionEngine *engine, int index);
-void popScriptContext(Value *stack, ExecutionEngine *engine);
-ReturnedValue deleteProperty(QV4::Function *function, const QV4::Value &base, const QV4::Value &index);
-ReturnedValue deleteName(Function *function, int name);
-void throwOnNullOrUndefined(ExecutionEngine *engine, const Value &v);
+// Last lowering phase: replace all JSOperations that are left with runtime calls. There is nothing
+// smart here, all that should have been done before this phase.
+class GenericLowering final
+{
+ Q_DISABLE_COPY(GenericLowering)
-} // Helpers namespace
-} // JIT namespace
+public:
+ GenericLowering(Function &f);
-#endif // V4_ENABLE_JIT
+ void lower();
-} // QV4 namespace
+private:
+ void replaceWithCall(Node *n);
+ void replaceWithVarArgsCall(Node *n);
+ static bool allUsesAsUnboxedBool(Node *n);
+
+ Function &function()
+ { return m_function; }
+
+ Graph *graph()
+ { return function().graph(); }
+
+ OperationBuilder *opBuilder()
+ { return graph()->opBuilder(); }
+
+private:
+ Function &m_function;
+};
+
+} // namespace IR
+} // namespace QV4
QT_END_NAMESPACE
-#endif // TEMPLATE_H
+#endif // QV4LOWERING_P_H
diff --git a/src/qml/jit/qv4mi.cpp b/src/qml/jit/qv4mi.cpp
new file mode 100644
index 0000000000..f0b172243d
--- /dev/null
+++ b/src/qml/jit/qv4mi.cpp
@@ -0,0 +1,251 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qloggingcategory.h>
+#include <private/qqmlglobal_p.h>
+
+#include "qv4mi_p.h"
+#include "qv4node_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcMI, "qt.v4.ir.mi")
+
+QString MIOperand::debugString() const
+{
+ switch (kind()) {
+ case Invalid: return QStringLiteral("<<INVALID>>");
+ case Constant: return ConstantPayload::debugString(constantValue());
+ case VirtualRegister: return QStringLiteral("vreg%1").arg(virtualRegister());
+ case EngineRegister: return QStringLiteral("engine");
+ case CppFrameRegister: return QStringLiteral("cppFrame");
+ case Function: return QStringLiteral("function");
+ case JSStackSlot: return QStringLiteral("jsstack[%1]").arg(stackSlot());
+ case BoolStackSlot: return QStringLiteral("bstack[%1]").arg(stackSlot());
+ case JumpTarget: return targetBlock() ? QStringLiteral("L%1").arg(targetBlock()->index())
+ : QStringLiteral("<<INVALID BLOCK>>");
+ default: Q_UNREACHABLE();
+ }
+}
+
+MIInstr *MIInstr::create(QQmlJS::MemoryPool *pool, Node *irNode, unsigned nOperands)
+{
+ return pool->New<MIInstr>(irNode, pool, nOperands);
+}
+
+static QString commentIndent(const QString &line)
+{
+ int spacing = std::max(70 - line.length(), 1);
+ return line + QString(spacing, QLatin1Char(' '));
+}
+
+static QString indent(int nr)
+{
+ QString s = nr == -1 ? QString() : QString::number(nr);
+ int padding = 6 - s.size();
+ if (padding > 0)
+ s = QString(padding, QLatin1Char(' ')) + s;
+ return s;
+}
+
+MIFunction::MIFunction(Function *irFunction)
+ : m_irFunction(irFunction)
+{}
+
+void MIFunction::renumberBlocks()
+{
+ for (size_t i = 0, ei = m_blocks.size(); i != ei; ++i) {
+ MIBlock *b = m_blocks[i];
+ b->setIndex(unsigned(i));
+ }
+}
+
+void MIFunction::renumberInstructions()
+{
+ int pos = 0;
+ for (MIBlock *b : m_blocks) {
+ for (MIInstr &instr : b->instructions()) {
+ pos += 2;
+ instr.setPosition(pos);
+ }
+ }
+}
+
+void MIFunction::dump(const QString &description) const
+{
+ if (!lcMI().isDebugEnabled())
+ return;
+
+ QString s = description + QLatin1String(":\n");
+ QString name;
+ if (auto n = irFunction()->v4Function()->name())
+ name = n->toQString();
+ if (name.isEmpty())
+ QString::asprintf("%p", static_cast<void *>(irFunction()->v4Function()));
+ QString line = QStringLiteral("function %1 {").arg(name);
+ auto loc = irFunction()->v4Function()->sourceLocation();
+ s += commentIndent(line) + QStringLiteral("; %1:%2:%3\n").arg(loc.sourceFile,
+ QString::number(loc.line),
+ QString::number(loc.column));
+ for (const MIBlock *b : blocks()) {
+ line = QStringLiteral("L%1").arg(b->index());
+ bool first = true;
+ if (!b->arguments().empty()) {
+ line += QLatin1Char('(');
+ for (const MIOperand &arg : b->arguments()) {
+ if (first)
+ first = false;
+ else
+ line += QStringLiteral(", ");
+ line += arg.debugString();
+ }
+ line += QLatin1Char(')');
+ }
+ line += QLatin1Char(':');
+ line = commentIndent(line) + QStringLiteral("; preds: ");
+ if (b->inEdges().isEmpty()) {
+ line += QStringLiteral("<none>");
+ } else {
+ bool first = true;
+ for (MIBlock *in : b->inEdges()) {
+ if (first)
+ first = false;
+ else
+ line += QStringLiteral(", ");
+ line += QStringLiteral("L%1").arg(in->index());
+ }
+ }
+ s += line + QLatin1Char('\n');
+ for (const MIInstr &i : b->instructions()) {
+ line = indent(i.position()) + QLatin1String(": ");
+ if (i.hasDestination())
+ line += i.destination().debugString() + QStringLiteral(" = ");
+ line += i.irNode()->operation()->debugString();
+ bool first = true;
+ for (const MIOperand &op : i.operands()) {
+ if (first)
+ first = false;
+ else
+ line += QLatin1Char(',');
+ line += QLatin1Char(' ') + op.debugString();
+ }
+ line = commentIndent(line) + QStringLiteral("; node-id: %1").arg(i.irNode()->id());
+ if (i.irNode()->operation()->needsBytecodeOffsets())
+ line += QStringLiteral(", bytecode-offset: %1").arg(irFunction()->nodeInfo(i.irNode())->currentInstructionOffset());
+ s += line + QLatin1Char('\n');
+ }
+ s += commentIndent(QString()) + QStringLiteral("; succs: ");
+ if (b->outEdges().isEmpty()) {
+ s += QStringLiteral("<none>");
+ } else {
+ bool first = true;
+ for (MIBlock *succ : b->outEdges()) {
+ if (first)
+ first = false;
+ else
+ s += QStringLiteral(", ");
+ s += QStringLiteral("L%1").arg(succ->index());
+ }
+ }
+ s += QLatin1Char('\n');
+ }
+ s += QLatin1Char('}');
+
+ for (const QStringRef &line : s.splitRef('\n'))
+ qCDebug(lcMI).noquote().nospace() << line;
+}
+
+unsigned MIFunction::extraJSSlots() const
+{
+ uint interpreterFrameSize = CppStackFrame::requiredJSStackFrameSize(irFunction()->v4Function());
+ if (m_jsSlotCount <= interpreterFrameSize)
+ return 0;
+ return m_jsSlotCount - interpreterFrameSize;
+}
+
+void MIFunction::setStartBlock(MIBlock *newStartBlock)
+{
+ auto it = std::find(m_blocks.begin(), m_blocks.end(), newStartBlock);
+ Q_ASSERT(it != m_blocks.end());
+ std::swap(*m_blocks.begin(), *it);
+}
+
+void MIFunction::setStackSlotCounts(unsigned dword, unsigned qword, unsigned js)
+{
+ m_vregCount = 0;
+ m_dwordSlotCount = dword;
+ m_qwordSlotCount = qword;
+ m_jsSlotCount = js;
+}
+
+void MIFunction::verifyCFG() const
+{
+ if (block(MIFunction::StartBlockIndex)->instructions().front().opcode() != Meta::Start)
+ qFatal("MIFunction block 0 is not the start block");
+
+ for (MIBlock *b : m_blocks) {
+ for (MIBlock *in : b->inEdges()) {
+ if (!in->outEdges().contains(b))
+ qFatal("block %u has incoming edge from block %u, "
+ "but does not appear in that block's outgoing edges",
+ b->index(), in->index());
+ }
+ for (MIBlock *out : b->outEdges()) {
+ if (!out->inEdges().contains(b))
+ qFatal("block %u has outgoing edge from block %u, "
+ "but does not appear in that block's incoming edges",
+ b->index(), out->index());
+ }
+ }
+}
+
+MIBlock *MIBlock::findEdgeTo(Operation::Kind target) const
+{
+ for (MIBlock *outEdge : outEdges()) {
+ if (outEdge->instructions().front().opcode() == target)
+ return outEdge;
+ }
+ return nullptr;
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4mi_p.h b/src/qml/jit/qv4mi_p.h
new file mode 100644
index 0000000000..f976d1dc94
--- /dev/null
+++ b/src/qml/jit/qv4mi_p.h
@@ -0,0 +1,627 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4MI_P_H
+#define QV4MI_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4global_p.h>
+#include <private/qv4ir_p.h>
+#include <private/qv4node_p.h>
+#include <private/qv4operation_p.h>
+
+#include <llvm/ADT/iterator.h>
+#include <llvm/ADT/iterator_range.h>
+#include <llvm/ADT/ilist.h>
+#include <llvm/ADT/ilist_node.h>
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+// This file contains the Machine Interface (MI) data structures, on which ultimately the assembler
+// will operate:
+
+class MIFunction; // containing all basic blocks, and a reference to the IR function
+
+class MIBlock; // containing an ordered sequence of instructions
+
+class MIInstr; // containing operands, and a reference to the IR node, that indicates which
+ // operation is represented by an instruction
+
+class MIOperand; // contains a description of where to get/put the input/result of an operation
+
+// A detail about the stack slots: there two stacks, the JS stack and the native stack. A frame on
+// the native stack is divided in two parts: the quad-word part and the double-word part. The
+// qword part holds 64bit values, like doubles, and pointers on 64bit architectures. The dword part
+// holds 32bit values, like int32s, booleans, and pointers on 32bit architectures. We need to know
+// the type of value a slot holds, because if we have to move it to the JS stack, we have to box it
+// correctly.
+class MIOperand final
+{
+public:
+ enum Kind {
+ Invalid = 0,
+ Constant,
+ VirtualRegister,
+
+ EngineRegister,
+ CppFrameRegister,
+ Function,
+
+ JSStackSlot,
+ BoolStackSlot,
+
+ JumpTarget,
+ };
+
+ using List = QQmlJS::FixedPoolArray<MIOperand>;
+
+public:
+ MIOperand() = default;
+
+ static MIOperand createConstant(Node *irNode)
+ {
+ MIOperand op;
+ op.m_kind = Constant;
+ op.m_irNode = irNode;
+ return op;
+ }
+
+ static MIOperand createVirtualRegister(Node *irNode, unsigned vreg)
+ {
+ MIOperand op;
+ op.m_kind = VirtualRegister;
+ op.m_irNode = irNode;
+ op.m_vreg = vreg;
+ return op;
+ }
+
+ static MIOperand createEngineRegister(Node *irNode)
+ {
+ MIOperand op;
+ op.m_kind = EngineRegister;
+ op.m_irNode = irNode;
+ return op;
+ }
+
+ static MIOperand createCppFrameRegister(Node *irNode)
+ {
+ MIOperand op;
+ op.m_kind = CppFrameRegister;
+ op.m_irNode = irNode;
+ return op;
+ }
+
+ static MIOperand createFunction(Node *irNode)
+ {
+ MIOperand op;
+ op.m_kind = Function;
+ op.m_irNode = irNode;
+ return op;
+ }
+
+ static MIOperand createJSStackSlot(Node *irNode, unsigned slot)
+ {
+ MIOperand op;
+ op.m_kind = JSStackSlot;
+ op.m_irNode = irNode;
+ op.m_slot = slot;
+ return op;
+ }
+
+ static MIOperand createBoolStackSlot(Node *irNode, unsigned slot)
+ {
+ MIOperand op;
+ op.m_kind = BoolStackSlot;
+ op.m_irNode = irNode;
+ op.m_slot = slot;
+ return op;
+ }
+
+ //### or name this createDeoptBlock?
+ static MIOperand createJumpTarget(Node *irNode, MIBlock *targetBlock)
+ {
+ MIOperand op;
+ op.m_kind = JumpTarget;
+ op.m_irNode = irNode;
+ op.m_targetBlock = targetBlock;
+ return op;
+ }
+
+ Kind kind() const
+ { return m_kind; }
+
+ bool isValid() const
+ { return m_kind != Invalid; }
+
+ bool isConstant() const
+ { return m_kind == Constant; }
+
+ bool isVirtualRegister() const
+ { return kind() == VirtualRegister; }
+
+ bool isEngineRegister() const
+ { return kind() == EngineRegister; }
+
+ bool isCppFrameRegister() const
+ { return kind() == CppFrameRegister; }
+
+ bool isFunction() const
+ { return kind() == Function; }
+
+ bool isJSStackSlot() const
+ { return kind() == JSStackSlot; }
+
+ bool isBoolStackSlot() const
+ { return kind() == BoolStackSlot; }
+
+ bool isStackSlot() const
+ { return isJSStackSlot() || isDWordSlot() || isQWordSlot(); }
+
+ bool isJumpTarget() const
+ { return kind() == JumpTarget; }
+
+ Node *irNode() const
+ { return m_irNode; }
+
+ inline Type nodeType(MIFunction *f) const;
+
+ QString debugString() const;
+
+ QV4::Value constantValue() const
+ {
+ Q_ASSERT(isConstant());
+ if (irNode()->opcode() == Meta::Undefined)
+ return QV4::Value::undefinedValue();
+ if (irNode()->opcode() == Meta::Empty)
+ return QV4::Value::emptyValue();
+ return ConstantPayload::get(*irNode()->operation())->value();
+ }
+
+ unsigned virtualRegister() const
+ { Q_ASSERT(isVirtualRegister()); return m_vreg; }
+
+ unsigned stackSlot() const
+ { Q_ASSERT(isStackSlot()); return m_slot; }
+
+ MIBlock *targetBlock() const
+ { Q_ASSERT(isJumpTarget()); return m_targetBlock; }
+
+ inline bool operator==(const MIOperand &other) const
+ {
+ if (kind() != other.kind())
+ return false;
+
+ if (isStackSlot())
+ return stackSlot() == other.stackSlot();
+
+ switch (kind()) {
+ case MIOperand::Invalid:
+ return !other.isValid();
+ case MIOperand::Constant:
+ return constantValue().asReturnedValue() == other.constantValue().asReturnedValue();
+ case MIOperand::VirtualRegister:
+ return virtualRegister() == other.virtualRegister();
+ case MIOperand::JumpTarget:
+ return targetBlock() == other.targetBlock();
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+ }
+
+ bool isDWordSlot() const
+ {
+ switch (kind()) {
+ case BoolStackSlot:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool isQWordSlot() const
+ {
+ switch (kind()) {
+ //### TODO: double slots
+ default:
+ return false;
+ }
+ }
+
+ bool overlaps(const MIOperand &other) const
+ {
+ if ((isDWordSlot() && other.isDWordSlot()) || (isQWordSlot() && other.isQWordSlot()))
+ ; // fine, these are the same
+ else if (kind() != other.kind())
+ return false;
+
+ if (isStackSlot())
+ return stackSlot() == other.stackSlot();
+
+ return false;
+ }
+
+private:
+ Node *m_irNode = nullptr;
+ union {
+ unsigned m_vreg;
+ unsigned m_slot;
+ MIBlock *m_targetBlock = nullptr;
+ };
+ Kind m_kind = Invalid;
+};
+
+template <typename NodeTy> struct MIInstrListParentType {};
+template <> struct MIInstrListParentType<MIInstr> { using type = MIBlock; };
+
+template <typename NodeTy> class MIInstrList;
+
+template <typename MISubClass>
+class MIInstrListTraits : public llvm::ilist_noalloc_traits<MISubClass>
+{
+protected:
+ using ListTy = MIInstrList<MISubClass>;
+ using iterator = typename llvm::simple_ilist<MISubClass>::iterator;
+ using ItemParentClass = typename MIInstrListParentType<MISubClass>::type;
+
+public:
+ MIInstrListTraits() = default;
+
+protected:
+ void setListOwner(ItemParentClass *listOwner)
+ { m_owner = listOwner; }
+
+private:
+ ItemParentClass *m_owner = nullptr;
+
+ /// getListOwner - Return the object that owns this list. If this is a list
+ /// of instructions, it returns the BasicBlock that owns them.
+ ItemParentClass *getListOwner() const {
+ return m_owner;
+ }
+
+ static ListTy &getList(ItemParentClass *Par) {
+ return Par->*(Par->getSublistAccess());
+ }
+
+ static MIInstrListTraits<MISubClass> *getSymTab(ItemParentClass *Par) {
+ return Par ? toPtr(Par->getValueSymbolTable()) : nullptr;
+ }
+
+public:
+ void addNodeToList(MISubClass *V) { V->setParent(getListOwner()); }
+ void removeNodeFromList(MISubClass *V) { V->setParent(nullptr); }
+ void transferNodesFromList(MIInstrListTraits &L2, iterator first,
+ iterator last);
+};
+
+template <class T>
+class MIInstrList: public llvm::iplist_impl<llvm::simple_ilist<T>, MIInstrListTraits<T>>
+{
+public:
+ MIInstrList(typename MIInstrListTraits<T>::ItemParentClass *owner)
+ { this->setListOwner(owner); }
+};
+
+class MIInstr final : public llvm::ilist_node_with_parent<MIInstr, MIBlock>
+{
+ Q_DISABLE_COPY_MOVE(MIInstr) // heap use only!
+
+protected:
+ friend class QQmlJS::MemoryPool;
+ MIInstr() : m_operands(nullptr, 0) {}
+
+ explicit MIInstr(Node *irNode, QQmlJS::MemoryPool *pool, unsigned nOperands)
+ : m_irNode(irNode)
+ , m_operands(pool, nOperands)
+ {}
+
+ ~MIInstr() = default;
+
+public:
+ static MIInstr *create(QQmlJS::MemoryPool *pool, Node *irNode, unsigned nOperands);
+
+ MIBlock *parent() const
+ { return m_parent; }
+
+ MIBlock *getParent() const // for ilist_node_with_parent
+ { return parent(); }
+
+ void setParent(MIBlock *parent)
+ { m_parent = parent; }
+
+ Node *irNode() const
+ { return m_irNode; }
+
+ Operation::Kind opcode() const
+ { return m_irNode->opcode(); }
+
+ int position() const
+ { return m_position; }
+
+ inline void insertBefore(MIInstr *insertPos);
+ inline void insertAfter(MIInstr *insertPos);
+ inline MIInstrList<MIInstr>::iterator eraseFromParent();
+
+ bool hasDestination() const
+ { return m_destination.isValid(); }
+
+ MIOperand destination() const
+ { return m_destination; }
+
+ void setDestination(const MIOperand &dest)
+ { m_destination = dest; }
+
+ const MIOperand &operand(unsigned index) const
+ { return m_operands.at(index); }
+
+ void setOperand(unsigned index, const MIOperand &op)
+ { m_operands.at(index) = op; }
+
+ MIOperand &operand(unsigned index)
+ { return m_operands.at(index); }
+
+ const MIOperand::List &operands() const
+ { return m_operands; }
+
+ MIOperand::List &operands()
+ { return m_operands; }
+
+private:
+ friend MIFunction;
+ void setPosition(int newPosition)
+ { m_position = newPosition; }
+
+private:
+ MIBlock *m_parent = nullptr;
+ Node *m_irNode = nullptr;
+ int m_position = -1;
+ MIOperand m_destination;
+ MIOperand::List m_operands;
+};
+
+class MIBlock final
+{
+ Q_DISABLE_COPY_MOVE(MIBlock)
+
+public:
+ using Index = unsigned;
+ enum : Index { InvalidIndex = std::numeric_limits<Index>::max() };
+
+ using MIInstructionList = MIInstrList<MIInstr>;
+
+ using InEdges = QVarLengthArray<MIBlock *, 4>;
+ using OutEdges = QVarLengthArray<MIBlock *, 2>;
+
+protected:
+ friend MIFunction;
+ explicit MIBlock(Index index)
+ : m_instructions(this),
+ m_index(index)
+ {}
+
+ void setIndex(Index newIndex)
+ { m_index = newIndex; }
+
+public:
+ ~MIBlock() = default;
+
+ const MIInstructionList &instructions() const
+ { return m_instructions; }
+
+ MIInstructionList &instructions()
+ { return m_instructions; }
+
+ static MIInstructionList MIBlock::*getSublistAccess(MIInstr * = nullptr)
+ { return &MIBlock::m_instructions; }
+
+ void addArgument(MIOperand &&arg)
+ { m_arguments.push_back(arg); }
+
+ const std::vector<MIOperand> &arguments() const
+ { return m_arguments; }
+
+ std::vector<MIOperand> &arguments()
+ { return m_arguments; }
+
+ void clearArguments()
+ { m_arguments.resize(0); }
+
+ const InEdges &inEdges() const
+ { return m_inEdges; }
+
+ void addInEdge(MIBlock *edge)
+ { m_inEdges.append(edge); }
+
+ const OutEdges &outEdges() const
+ { return m_outEdges; }
+
+ void addOutEdge(MIBlock *edge)
+ { m_outEdges.append(edge); }
+
+ Index index() const
+ { return m_index; }
+
+ MIBlock *findEdgeTo(Operation::Kind target) const;
+
+ bool isDeoptBlock() const
+ { return m_isDeoptBlock; }
+
+ void markAsDeoptBlock()
+ { m_isDeoptBlock = true; }
+
+private:
+ std::vector<MIOperand> m_arguments;
+ MIInstructionList m_instructions;
+ InEdges m_inEdges;
+ OutEdges m_outEdges;
+ Index m_index;
+ bool m_isDeoptBlock = false;
+};
+
+class MIFunction final
+{
+ Q_DISABLE_COPY_MOVE(MIFunction)
+
+public:
+ static constexpr MIBlock::Index StartBlockIndex = 0;
+
+public:
+ MIFunction(Function *irFunction);
+ ~MIFunction()
+ { qDeleteAll(m_blocks); }
+
+ Function *irFunction() const
+ { return m_irFunction; }
+
+ void setStartBlock(MIBlock *newStartBlock);
+ void renumberBlocks();
+ void renumberInstructions();
+
+ void dump(const QString &description) const;
+
+ size_t blockCount() const
+ { return blocks().size(); }
+
+ MIBlock *block(MIBlock::Index index) const
+ { return m_blocks[index]; }
+
+ const std::vector<MIBlock *> &blocks() const
+ { return m_blocks; }
+
+ MIBlock *addBlock()
+ {
+ auto *b = new MIBlock(unsigned(m_blocks.size()));
+ m_blocks.push_back(b);
+ return b;
+ }
+
+ void setBlockOrder(const std::vector<MIBlock *> &newSequence)
+ { m_blocks = newSequence; }
+
+ unsigned vregCount() const
+ { return m_vregCount; }
+
+ void setVregCount(unsigned vregCount)
+ { m_vregCount = vregCount; }
+
+ unsigned dwordSlotCount() const
+ { return m_dwordSlotCount; }
+
+ unsigned qwordSlotCount() const
+ { return m_qwordSlotCount; }
+
+ unsigned jsSlotCount() const
+ { return m_jsSlotCount; }
+
+ unsigned extraJSSlots() const;
+
+ void setStackSlotCounts(unsigned dword, unsigned qword, unsigned js);
+
+ void verifyCFG() const;
+
+private:
+ Function *m_irFunction = nullptr;
+ std::vector<MIBlock *> m_blocks;
+ unsigned m_vregCount = 0;
+ unsigned m_dwordSlotCount = 0;
+ unsigned m_qwordSlotCount = 0;
+ unsigned m_jsSlotCount = 0;
+};
+
+Type MIOperand::nodeType(MIFunction *f) const
+{
+ return f->irFunction()->nodeInfo(irNode())->type();
+}
+
+inline uint qHash(const MIOperand &key, uint seed)
+{
+ uint h = ::qHash(key.kind(), seed);
+ switch (key.kind()) {
+ case MIOperand::VirtualRegister:
+ h ^= key.virtualRegister();
+ break;
+ case MIOperand::BoolStackSlot: Q_FALLTHROUGH();
+ case MIOperand::JSStackSlot:
+ h ^= key.stackSlot();
+ break;
+ default:
+ qFatal("%s: cannot hash %s", Q_FUNC_INFO, key.debugString().toUtf8().constData());
+ }
+ return h;
+}
+
+void MIInstr::insertBefore(MIInstr *insertPos)
+{
+ insertPos->parent()->instructions().insert(insertPos->getIterator(), this);
+}
+
+void MIInstr::insertAfter(MIInstr *insertPos)
+{
+ insertPos->parent()->instructions().insert(++insertPos->getIterator(), this);
+}
+
+MIInstrList<MIInstr>::iterator MIInstr::eraseFromParent()
+{
+ auto p = parent();
+ setParent(nullptr);
+ return p->instructions().erase(getIterator());
+}
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4MI_P_H
diff --git a/src/qml/jit/qv4miblockset_p.h b/src/qml/jit/qv4miblockset_p.h
new file mode 100644
index 0000000000..5f814b99e0
--- /dev/null
+++ b/src/qml/jit/qv4miblockset_p.h
@@ -0,0 +1,291 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4MIBLOCKSET_P_H
+#define QV4MIBLOCKSET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4mi_p.h"
+#include "qv4util_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class MIBlockSet
+{
+ using Flags = BitVector;
+
+ QVarLengthArray<MIBlock::Index, 8> blockNumbers;
+ Flags *blockFlags = nullptr;
+ MIFunction *function = nullptr;
+ enum { MaxVectorCapacity = 8 };
+
+public:
+ class const_iterator;
+ friend class const_iterator;
+
+public:
+ MIBlockSet(MIFunction *f = nullptr)
+ {
+ if (f)
+ init(f);
+ }
+
+ MIBlockSet(MIBlockSet &&other) noexcept
+ {
+ std::swap(blockNumbers, other.blockNumbers);
+ std::swap(blockFlags, other.blockFlags);
+ std::swap(function, other.function);
+ }
+
+ MIBlockSet(const MIBlockSet &other)
+ : function(other.function)
+ {
+ if (other.blockFlags)
+ blockFlags = new Flags(*other.blockFlags);
+ blockNumbers = other.blockNumbers;
+ }
+
+ MIBlockSet &operator=(const MIBlockSet &other)
+ {
+ if (blockFlags) {
+ delete blockFlags;
+ blockFlags = nullptr;
+ }
+ function = other.function;
+ if (other.blockFlags)
+ blockFlags = new Flags(*other.blockFlags);
+ blockNumbers = other.blockNumbers;
+ return *this;
+ }
+
+ MIBlockSet &operator=(MIBlockSet &&other) noexcept
+ {
+ if (&other != this) {
+ std::swap(blockNumbers, other.blockNumbers);
+
+ delete blockFlags;
+ blockFlags = other.blockFlags;
+ other.blockFlags = nullptr;
+
+ function = other.function;
+ }
+ return *this;
+ }
+
+ ~MIBlockSet()
+ {
+ delete blockFlags;
+ }
+
+ void init(MIFunction *f)
+ {
+ Q_ASSERT(!function);
+ Q_ASSERT(f);
+ function = f;
+ }
+
+ bool empty() const;
+
+ void insert(MIBlock *bb)
+ {
+ Q_ASSERT(function);
+
+ if (blockFlags) {
+ blockFlags->setBit(bb->index());
+ return;
+ }
+
+ for (unsigned int blockNumber : qAsConst(blockNumbers)) {
+ if (blockNumber == bb->index())
+ return;
+ }
+
+ if (blockNumbers.size() == MaxVectorCapacity) {
+ blockFlags = new Flags(int(function->blockCount()), false);
+ for (unsigned int blockNumber : qAsConst(blockNumbers)) {
+ blockFlags->setBit(int(blockNumber));
+ }
+ blockNumbers.clear();
+ blockFlags->setBit(int(bb->index()));
+ } else {
+ blockNumbers.append(bb->index());
+ }
+ }
+
+ void remove(MIBlock *bb)
+ {
+ Q_ASSERT(function);
+
+ if (blockFlags) {
+ blockFlags->clearBit(bb->index());
+ return;
+ }
+
+ for (int i = 0; i < blockNumbers.size(); ++i) {
+ if (blockNumbers[i] == bb->index()) {
+ blockNumbers.remove(i);
+ return;
+ }
+ }
+ }
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ void collectValues(std::vector<MIBlock *> &bbs) const;
+
+ bool contains(MIBlock *bb) const
+ {
+ Q_ASSERT(function);
+
+ if (blockFlags)
+ return blockFlags->at(bb->index());
+
+ for (unsigned int blockNumber : blockNumbers) {
+ if (blockNumber == bb->index())
+ return true;
+ }
+
+ return false;
+ }
+};
+
+class MIBlockSet::const_iterator
+{
+ const MIBlockSet &set;
+ // ### These two members could go into a union, but clang won't compile
+ // (https://codereview.qt-project.org/#change,74259)
+ QVarLengthArray<MIBlock::Index, 8>::const_iterator numberIt;
+ MIBlock::Index flagIt;
+
+ friend class MIBlockSet;
+ const_iterator(const MIBlockSet &set, bool end)
+ : set(set)
+ {
+ if (end || !set.function) {
+ if (!set.blockFlags)
+ numberIt = set.blockNumbers.end();
+ else
+ flagIt = set.blockFlags->size();
+ } else {
+ if (!set.blockFlags)
+ numberIt = set.blockNumbers.begin();
+ else
+ findNextWithFlags(0);
+ }
+ }
+
+ void findNextWithFlags(int start)
+ {
+ flagIt = MIBlock::Index(set.blockFlags->findNext(start, true, /*wrapAround = */false));
+ Q_ASSERT(flagIt <= MIBlock::Index(set.blockFlags->size()));
+ }
+
+public:
+ MIBlock *operator*() const
+ {
+ if (!set.blockFlags)
+ return set.function->block(*numberIt);
+
+ Q_ASSERT(flagIt <= set.function->blockCount());
+ return set.function->block(flagIt);
+
+ }
+
+ bool operator==(const const_iterator &other) const
+ {
+ if (&set != &other.set)
+ return false;
+ if (!set.blockFlags)
+ return numberIt == other.numberIt;
+ return flagIt == other.flagIt;
+ }
+
+ bool operator!=(const const_iterator &other) const
+ { return !(*this == other); }
+
+ const_iterator &operator++()
+ {
+ if (!set.blockFlags)
+ ++numberIt;
+ else
+ findNextWithFlags(flagIt + 1);
+
+ return *this;
+ }
+};
+
+inline bool MIBlockSet::empty() const
+{ return begin() == end(); }
+
+inline MIBlockSet::const_iterator MIBlockSet::begin() const
+{ return const_iterator(*this, false); }
+
+inline MIBlockSet::const_iterator MIBlockSet::end() const
+{ return const_iterator(*this, true); }
+
+inline void MIBlockSet::collectValues(std::vector<MIBlock *> &bbs) const
+{
+ Q_ASSERT(function);
+
+ for (auto it : *this)
+ bbs.push_back(it);
+}
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4MIBLOCKSET_P_H
diff --git a/src/qml/jit/qv4node.cpp b/src/qml/jit/qv4node.cpp
new file mode 100644
index 0000000000..e059e9fef6
--- /dev/null
+++ b/src/qml/jit/qv4node.cpp
@@ -0,0 +1,215 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4node_p.h"
+#include "qv4graph_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Node *Node::create(Node::MemoryPool *pool, Node::Id id, const Operation *op, size_t nInputs,
+ Node *const *inputs, bool inputsAreExtensible)
+{
+ size_t capacity = nInputs;
+ if (inputsAreExtensible)
+ capacity += 3;
+
+ Node *node = new (pool->allocate(sizeof(Node))) Node(pool, id, op, unsigned(nInputs),
+ int(capacity));
+ for (uint i = 0; i < capacity; ++i)
+ new (&node->m_inputs[int(i)]) Use(node);
+ for (size_t i = 0; i < nInputs; ++i) {
+ Q_ASSERT(inputs[i] != nullptr);
+ node->replaceInput(unsigned(i), inputs[i]);
+ }
+
+ return node;
+}
+
+void Node::addInput(MemoryPool *pool, Node *in)
+{
+ Q_ASSERT(in);
+ ++m_nInputs;
+ if (m_nInputs >= unsigned(m_inputs.size())) {
+ QQmlJS::FixedPoolArray<Use> oldInputs = m_inputs;
+ m_inputs = QQmlJS::FixedPoolArray<Use>(pool, int(m_nInputs + 3));
+ for (Use &input : m_inputs)
+ new (&input) Use(this);
+ for (int i = 0, ei = oldInputs.size(); i != ei; ++i) {
+ Node *in = oldInputs[i].m_input;
+ oldInputs[i].set(nullptr);
+ m_inputs[i].set(in);
+ }
+ }
+ m_inputs.at(int(m_nInputs - 1)).set(in);
+}
+
+void Node::removeInput(unsigned index)
+{
+ Q_ASSERT(index < inputCount());
+ for (unsigned i = index, ei = inputCount(); i < ei - 1; ++i)
+ replaceInput(i, input(i + 1));
+ trimInputCount(inputCount() - 1);
+}
+
+void Node::removeInputs(unsigned start, unsigned count)
+{
+ for (unsigned idx = start; idx < start + count; ++idx)
+ m_inputs.at(int(idx)).set(nullptr);
+}
+
+void Node::removeAllInputs()
+{
+ removeInputs(0, inputCount());
+}
+
+void Node::trimInputCount(unsigned newCount)
+{
+ unsigned currentCount = inputCount();
+ if (newCount == currentCount)
+ return;
+ Q_ASSERT(newCount < currentCount);
+ removeInputs(newCount, currentCount - newCount);
+ m_nInputs = newCount;
+}
+
+void Node::removeExceptionHandlerUse()
+{
+ for (Use* use = m_firstUse; use; use = use->m_next) {
+ if (use->m_input->opcode() == Meta::OnException) {
+ use->set(nullptr);
+ break;
+ }
+ }
+}
+
+void Node::insertInput(Node::MemoryPool *pool, unsigned index, Node *newInput)
+{
+ Q_ASSERT(index < inputCount());
+ addInput(pool, input(inputCount() - 1));
+ for (unsigned i = inputCount() - 1; i > index; --i)
+ replaceInput(i, input(i - 1));
+ replaceInput(index, newInput);
+}
+
+void Node::replaceAllUsesWith(Node *replacement)
+{
+ for (Use *use = m_firstUse; use; ) {
+ Use *next = use->m_next;
+ const unsigned inIdx = use->inputIndex();
+ use->user()->replaceInput(inIdx, replacement);
+ use = next;
+ }
+}
+
+void Node::replaceUses(Node *newValueInput, Node *newEffectInput, Node *newControlInput)
+{
+ for (Use *use = m_firstUse; use; ) {
+ Use *next = use->m_next;
+ const Operation *inOp = use->user()->operation();
+ const unsigned inIdx = use->inputIndex();
+ if (inIdx < inOp->valueInputCount())
+ use->user()->replaceInput(inIdx, newValueInput);
+ else if (inIdx < inOp->indexOfFirstControl())
+ use->user()->replaceInput(inIdx, newEffectInput);
+ else
+ use->user()->replaceInput(inIdx, newControlInput);
+ use = next;
+ }
+}
+
+Node *Node::firstValueUse()
+{
+ for (auto it = uses().begin(), eit = uses().end(); it != eit; ++it) {
+ if (it.isUsedAsValue())
+ return *it;
+ }
+ return nullptr;
+}
+
+Node::Node(MemoryPool *pool, Node::Id id, const Operation *op, unsigned nInputs, int capacity)
+ : m_op(op)
+ , m_inputs(pool, capacity)
+ , m_nInputs(nInputs)
+ , m_id(id)
+{
+}
+
+NodeWorkList::NodeWorkList(const Graph *g)
+ : m_nodeState(g->nodeCount(), Unvisited)
+{ m_worklist.reserve(64); }
+
+void NodeWorkList::reset()
+{
+ std::fill(m_nodeState.begin(), m_nodeState.end(), Unvisited);
+ m_worklist.clear();
+ if (m_worklist.capacity() < 64)
+ m_worklist.reserve(64);
+}
+
+NodeCollector::NodeCollector(const Graph *g, bool collectUses, bool skipFramestate)
+{
+ markReachable(g->endNode());
+ for (size_t i = 0; i < m_reachable.size(); ++i) { // _reachable.size() is on purpose!
+ Node *n = m_reachable.at(i);
+ for (auto input : n->inputs()) {
+ if (input == nullptr)
+ continue;
+ if (isReachable(input->id()))
+ continue;
+ if (skipFramestate && input->opcode() == Meta::FrameState)
+ continue;
+ markReachable(input);
+ }
+
+ if (collectUses) {
+ for (Node *use : n->uses()) {
+ if (use && !isReachable(use->id()))
+ markReachable(use);
+ }
+ }
+ }
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4node_p.h b/src/qml/jit/qv4node_p.h
new file mode 100644
index 0000000000..76065fb1bc
--- /dev/null
+++ b/src/qml/jit/qv4node_p.h
@@ -0,0 +1,626 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4NODE_P_H
+#define QV4NODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qqmljsmemorypool_p.h>
+#include <private/qv4global_p.h>
+#include <private/qv4operation_p.h>
+#include "qv4util_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class Use
+{
+ Q_DISABLE_COPY_MOVE(Use)
+
+public:
+ Use(Node *user)
+ : m_user(user)
+ {}
+
+ ~Use()
+ {
+ if (m_input)
+ removeFromList();
+ }
+
+ operator Node *() const { return m_input; }
+ Node *input() const { return m_input; }
+ Node *user() const { return m_user; }
+
+ inline void set(Node *newInput);
+
+ void validate() const
+ {
+ Q_ASSERT(m_user);
+ if (m_input) {
+ if (m_prev != nullptr)
+ Q_ASSERT(*m_prev == this);
+ if (m_next) {
+ Q_ASSERT(m_next->m_input == m_input);
+ Q_ASSERT(m_next->m_prev == &m_next);
+ m_next->validate();
+ }
+ }
+ }
+
+ inline int inputIndex() const;
+
+protected:
+ friend class Node;
+
+ void addToList(Use **list) {
+ validate();
+ m_next = *list;
+ if (m_next)
+ m_next->m_prev = &m_next;
+ m_prev = list;
+ *list = this;
+ validate();
+ }
+
+ void removeFromList() {
+ validate();
+ Use **newPrev = m_prev;
+ *newPrev = m_next;
+ m_prev = nullptr;
+ if (m_next)
+ m_next->m_prev = newPrev;
+ m_next = nullptr;
+ m_input = nullptr;
+ validate();
+ }
+
+private:
+ Node *m_input = nullptr;
+ Node *m_user = nullptr;
+ Use *m_next = nullptr;
+ Use **m_prev = nullptr;
+};
+
+// A node represents an calculation, action, or marker in the graph. Each node has an operation,
+// input dependencies and uses. The operation indicates what kind of node it is, e.g.: JSAdd,
+// Constant, Region, and so on. Two nodes can have the same operation, but different inputs.
+// For example, the expressions 1 + 2 and 3 + 4 will each have a node with an JSAdd operation
+// (which is exactly the same operation for both nodes), but the nodes have different inputs (1, and
+// 2 in the first expression, while the second operation has 3 and 4 as inputs).
+class Node final
+{
+ Q_DISABLE_COPY_MOVE(Node)
+
+public:
+ using Id = uint32_t;
+ using MemoryPool = QQmlJS::MemoryPool;
+ class Inputs;
+
+public:
+ static Node *create(MemoryPool *pool, Id id, const Operation *op, size_t nInputs,
+ Node * const *inputs, bool inputsAreExtensible = false);
+ ~Node() = delete;
+
+ inline bool isDead() const;
+ inline void kill();
+
+ Id id() const { return m_id; }
+
+ const Operation *operation() const
+ { return m_op; }
+
+ void setOperation(const Operation *op)
+ { m_op = op; }
+
+ Operation::Kind opcode() const
+ { return operation()->kind(); }
+
+ inline Inputs inputs() const;
+ void addInput(MemoryPool *pool, Node *in);
+ void removeInput(unsigned index);
+ void removeInputs(unsigned start, unsigned count);
+ void removeAllInputs();
+ uint32_t inputCount() const
+ { return m_nInputs; }
+ void trimInputCount(unsigned newCount);
+
+ void removeExceptionHandlerUse();
+
+ Node *input(unsigned idx) const
+ {
+ Q_ASSERT(idx < inputCount());
+ return m_inputs.at(idx);
+ }
+
+ Node *effectInput(unsigned effectIndex = 0) const
+ {
+ if (operation()->effectInputCount() == 0)
+ return nullptr;
+ Q_ASSERT(effectIndex < operation()->effectInputCount());
+ return input(operation()->indexOfFirstEffect() + effectIndex);
+ }
+
+ Node *controlInput(unsigned controlIndex = 0) const
+ {
+ if (operation()->controlInputCount() == 0)
+ return nullptr;
+ Q_ASSERT(controlIndex < operation()->controlInputCount());
+ return input(operation()->indexOfFirstControl() + controlIndex);
+ }
+
+ Node *frameStateInput() const
+ {
+ if (operation()->hasFrameStateInput())
+ return input(operation()->indexOfFrameStateInput());
+ return nullptr;
+ }
+
+ void setFrameStateInput(Node *newFramestate)
+ {
+ if (operation()->hasFrameStateInput())
+ replaceInput(operation()->indexOfFrameStateInput(), newFramestate);
+ }
+
+ void insertInput(MemoryPool *pool, unsigned index, Node *newInput);
+
+ void replaceInput(Node *oldIn, Node *newIn)
+ {
+ for (unsigned i = 0, ei = inputCount(); i != ei; ++i) {
+ if (input(i) == oldIn)
+ replaceInput(i, newIn);
+ }
+ }
+
+ void replaceInput(unsigned idx, Node *newIn)
+ {
+ m_inputs[idx].set(newIn);
+ }
+
+ class Uses
+ {
+ public:
+ explicit Uses(Node *node)
+ : m_node(node)
+ {}
+
+ class const_iterator;
+ inline const_iterator begin() const;
+ inline const_iterator end() const;
+
+ bool isEmpty() const;
+
+ private:
+ Node *m_node;
+ };
+
+ Uses uses() { return Uses(this); }
+ bool hasUses() const { return m_firstUse != nullptr; }
+ unsigned useCount() const
+ {
+ unsigned cnt = 0;
+ for (Use *it = m_firstUse; it; it = it->m_next)
+ ++cnt;
+ return cnt;
+ }
+ void replaceAllUsesWith(Node *replacement);
+ void replaceUses(Node *newValueInput, Node *newEffectInput, Node *newControlInput);
+
+ Node *firstValueUse();
+
+private: // types and utility methods
+ friend class Use;
+ Node(MemoryPool *pool, Id id, const Operation *op, unsigned nInputs, int capacity);
+
+private: // fields
+ Use *m_firstUse = nullptr;
+ const Operation *m_op = nullptr;
+ QQmlJS::FixedPoolArray<Use> m_inputs;
+ unsigned m_nInputs = 0;
+ Id m_id = 0;
+};
+
+void Use::set(Node *newInput)
+{
+ if (m_input)
+ removeFromList();
+ m_input = newInput;
+ if (newInput)
+ addToList(&newInput->m_firstUse);
+}
+
+class Node::Inputs final
+{
+public:
+ using value_type = Node *;
+
+ class const_iterator;
+ inline const_iterator begin() const;
+ inline const_iterator end() const;
+
+ bool empty() const
+ { return m_nInputs == 0; }
+
+ unsigned count() const
+ { return m_nInputs; }
+
+ explicit Inputs(const Use *inputs, unsigned nInputs)
+ : m_inputs(inputs), m_nInputs(nInputs)
+ {}
+
+private:
+ const Use *m_inputs = nullptr;
+ unsigned m_nInputs = 0;
+};
+
+class Node::Inputs::const_iterator final
+{
+public:
+ using iterator_category = std::forward_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = Node *;
+ using pointer = const value_type *;
+ using reference = value_type &;
+
+ Node *operator*() const
+ { return m_inputs->m_input; }
+
+ bool operator==(const const_iterator &other) const
+ { return m_inputs == other.m_inputs; }
+
+ bool operator!=(const const_iterator &other) const
+ { return !(*this == other); }
+
+ const_iterator &operator++()
+ { ++m_inputs; return *this; }
+
+ const_iterator& operator+=(difference_type offset)
+ { m_inputs += offset; return *this; }
+
+ const_iterator operator+(difference_type offset) const
+ { return const_iterator(m_inputs + offset); }
+
+ difference_type operator-(const const_iterator &other) const
+ { return m_inputs - other.m_inputs; }
+
+private:
+ friend class Node::Inputs;
+
+ explicit const_iterator(const Use *inputs)
+ : m_inputs(inputs)
+ {}
+
+ const Use *m_inputs;
+};
+
+Node::Inputs::const_iterator Node::Inputs::begin() const
+{ return const_iterator(m_inputs); }
+
+Node::Inputs::const_iterator Node::Inputs::end() const
+{ return const_iterator(m_inputs + m_nInputs); }
+
+Node::Inputs Node::inputs() const
+{
+ return Inputs(m_inputs.begin(), m_nInputs);
+}
+
+class Node::Uses::const_iterator final
+{
+public:
+ using iterator_category = std::forward_iterator_tag;
+ using difference_type = int;
+ using value_type = Node *;
+ using pointer = Node **;
+ using reference = Node *&;
+
+ Node *operator*() const
+ { return m_current->user(); }
+
+ bool operator==(const const_iterator &other) const
+ { return other.m_current == m_current; }
+
+ bool operator!=(const const_iterator &other) const
+ { return other.m_current != m_current; }
+
+ const_iterator &operator++()
+ { m_current = m_next; setNext(); return *this; }
+
+ unsigned inputIndex() const
+ { return m_current->inputIndex(); }
+
+ bool isUsedAsValue() const
+ { return inputIndex() < operator*()->operation()->valueInputCount(); }
+
+ bool isUsedAsControl() const
+ { return operator*()->operation()->indexOfFirstControl() <= inputIndex(); }
+
+private:
+ friend class Node::Uses;
+
+ const_iterator() = default;
+
+ explicit const_iterator(Node* node)
+ : m_current(node->m_firstUse)
+ { setNext(); }
+
+ void setNext()
+ {
+ if (m_current)
+ m_next = m_current->m_next;
+ else
+ m_next = nullptr;
+ }
+
+private:
+ Use *m_current = nullptr;
+ Use *m_next = nullptr;
+};
+
+Node::Uses::const_iterator Node::Uses::begin() const
+{ return const_iterator(this->m_node); }
+
+Node::Uses::const_iterator Node::Uses::end() const
+{ return const_iterator(); }
+
+int Use::inputIndex() const
+{
+ if (!m_user)
+ return -1;
+ return int(this - m_user->m_inputs.begin());
+}
+
+bool Node::isDead() const
+{
+ Inputs in = inputs();
+ return !in.empty() && *in.begin() == nullptr;
+}
+
+void Node::kill()
+{
+ removeAllInputs();
+}
+
+class NodeWorkList final
+{
+ enum State: uint8_t {
+ Unvisited = 0,
+ Queued,
+ Visited,
+ };
+
+public:
+ NodeWorkList(const Graph *g);
+
+ void reset();
+
+ bool enqueue(Node *n)
+ {
+ State &s = nodeState(n);
+ if (s == Queued || s == Visited)
+ return false;
+
+ m_worklist.push_back(n);
+ s = Queued;
+ return true;
+ }
+
+ void enqueue(const std::vector<Node *> &nodes)
+ {
+ m_worklist.insert(m_worklist.end(), nodes.begin(), nodes.end());
+ for (Node *n : nodes)
+ nodeState(n) = Queued;
+ }
+
+ void reEnqueue(Node *n)
+ {
+ if (!n)
+ return;
+ State &s = nodeState(n);
+ if (s == Queued)
+ return;
+ s = Queued;
+ m_worklist.push_back(n);
+ }
+
+ void enqueueAllInputs(Node *n)
+ {
+ for (Node *input : n->inputs())
+ enqueue(input);
+ }
+
+ void reEnqueueAllInputs(Node *n)
+ {
+ for (Node *input : n->inputs())
+ reEnqueue(input);
+ }
+
+ void enqueueValueInputs(Node *n)
+ {
+ for (unsigned i = 0, ei = n->operation()->valueInputCount(); i != ei; ++i)
+ enqueue(n->input(i));
+ }
+
+ void enqueueEffectInputs(Node *n)
+ {
+ for (unsigned i = n->operation()->indexOfFirstEffect(), ei = n->operation()->effectInputCount(); i != ei; ++i)
+ enqueue(n->input(i));
+ }
+
+ void enqueueAllUses(Node *n)
+ {
+ for (Node *use : n->uses())
+ enqueue(use);
+ }
+
+ Node *dequeueNextNodeForVisiting()
+ {
+ while (!m_worklist.empty()) {
+ Node *n = m_worklist.back();
+ m_worklist.pop_back();
+ State &s = nodeState(n);
+ Q_ASSERT(s == Queued);
+ s = Visited;
+ return n;
+ }
+
+ return nullptr;
+ }
+
+ bool isVisited(Node *n) const
+ { return nodeState(n) == Visited; }
+
+ bool isEmpty() const
+ { return m_worklist.empty(); }
+
+ QString status(Node *n) const
+ {
+ QString s = QStringLiteral("status for node %1: ").arg(n->id());
+ switch (nodeState(n)) {
+ case Queued: s += QLatin1String("queued"); break;
+ case Visited: s += QLatin1String("visited"); break;
+ case Unvisited: s += QLatin1String("unvisited"); break;
+ }
+ return s;
+ }
+
+private:
+ State &nodeState(Node *n)
+ {
+ const unsigned position(n->id());
+ if (position >= m_nodeState.size())
+ m_nodeState.resize(position + 1, Unvisited);
+
+ return m_nodeState[position];
+ }
+
+ State nodeState(Node *n) const
+ { return m_nodeState[unsigned(n->id())]; }
+
+private:
+ std::vector<Node *> m_worklist;
+ std::vector<State> m_nodeState;
+};
+
+class NodeInfo
+{
+public:
+ enum { NoInstructionOffset = -1 };
+
+public:
+ NodeInfo() = default;
+
+ Type type() const { return m_type; }
+ void setType(Type t) { m_type = t; }
+
+ int currentInstructionOffset() const
+ { return m_currentInstructionOffset; }
+
+ int nextInstructionOffset() const
+ { return m_nextInstructionOffset; }
+
+ void setBytecodeOffsets(int current, int next)
+ {
+ Q_ASSERT(current != NoInstructionOffset);
+ Q_ASSERT(next != NoInstructionOffset);
+ m_currentInstructionOffset = current;
+ m_nextInstructionOffset = next;
+ }
+
+private:
+ Type m_type;
+ int m_currentInstructionOffset = NoInstructionOffset;
+ int m_nextInstructionOffset = NoInstructionOffset;
+};
+
+class NodeCollector
+{
+public:
+ NodeCollector(const Graph *g, bool collectUses = false, bool skipFramestate = false);
+
+ const std::vector<Node *> &reachable() const
+ { return m_reachable; }
+
+ void sortById()
+ {
+ std::sort(m_reachable.begin(), m_reachable.end(), [](Node *n1, Node *n2) {
+ return n1->id() < n2->id();
+ });
+ }
+
+ bool isReachable(Node::Id nodeId) const
+ {
+ if (nodeId >= Node::Id(m_isReachable.size()))
+ return false;
+ return m_isReachable.at(int(nodeId));
+ }
+
+ void markReachable(Node *node)
+ {
+ auto nodeId = node->id();
+ m_reachable.push_back(node);
+ if (nodeId >= Node::Id(m_isReachable.size()))
+ m_isReachable.resize(int(nodeId + 1), false);
+ m_isReachable.setBit(int(nodeId));
+ }
+
+private:
+ std::vector<Node *> m_reachable;
+ BitVector m_isReachable;
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4NODE_P_H
diff --git a/src/qml/jit/qv4operation.cpp b/src/qml/jit/qv4operation.cpp
new file mode 100644
index 0000000000..acd5328fd0
--- /dev/null
+++ b/src/qml/jit/qv4operation.cpp
@@ -0,0 +1,770 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4operation_p.h"
+#include "qv4runtimesupport_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+OperationBuilder::OperationBuilder(QQmlJS::MemoryPool *graphPool)
+ : m_graphPool(graphPool)
+{}
+
+OperationBuilder *OperationBuilder::create(QQmlJS::MemoryPool *pool)
+{
+ return pool->New<OperationBuilder>(pool);
+}
+
+Operation *OperationBuilder::getConstant(Value v)
+{
+ Type t;
+ switch (v.type()) {
+ case Value::Undefined_Type: t = Type::undefinedType(); break;
+ case Value::Integer_Type: t = Type::int32Type(); break;
+ case Value::Boolean_Type: t = Type::booleanType(); break;
+ case Value::Null_Type: t = Type::nullType(); break;
+ case Value::Double_Type: t = Type::doubleType(); break;
+ case Value::Managed_Type: t = Type::objectType(); break;
+ default:
+ if (v.isEmpty())
+ t = Type::emptyType();
+ else
+ Q_UNREACHABLE();
+ }
+ return OperationWithPayload<ConstantPayload>::create(
+ m_graphPool, Meta::Constant, 0, 0, 0, 1, 0, 0, t, Operation::NoFlags,
+ ConstantPayload(v));
+}
+
+Operation *OperationBuilder::getParam(unsigned index, const Function::StringId name)
+{
+ return OperationWithPayload<ParameterPayload>::create(m_graphPool, Meta::Parameter,
+ 1, 0, 0,
+ 1, 0, 0,
+ Type::anyType(), Operation::NoFlags,
+ ParameterPayload(index, name));
+}
+
+Operation *OperationBuilder::getRegion(unsigned nControlInputs) //### cache common operands in the static pool
+{
+ return Operation::create(m_graphPool, Meta::Region,
+ 0, 0, uint16_t(nControlInputs),
+ 0, 0, 1,
+ Type(), Operation::NoFlags);
+}
+
+Operation *OperationBuilder::getPhi(unsigned nValueInputs) //### cache common operands in the static pool
+{
+ return Operation::create(m_graphPool, Meta::Phi,
+ uint16_t(nValueInputs), 0, 1,
+ 1, 0, 0,
+ Type::anyType(), Operation::NoFlags);
+}
+
+Operation *OperationBuilder::getEffectPhi(unsigned nEffectInputs) //### cache common operands in the static pool
+{
+ return Operation::create(m_graphPool, Meta::EffectPhi,
+ 0, uint16_t(nEffectInputs), 1,
+ 0, 1, 0,
+ Type(), Operation::NoFlags);
+}
+
+Operation *OperationBuilder::getUnwindDispatch(unsigned nContinuations, int unwindHandlerOffset,
+ int fallthroughSuccessor)
+{
+ return OperationWithPayload<UnwindDispatchPayload>::create(
+ m_graphPool, Meta::UnwindDispatch,
+ 0, 1, 1, 0, nContinuations, nContinuations,
+ Type(), Operation::NoFlags,
+ UnwindDispatchPayload(unwindHandlerOffset,
+ fallthroughSuccessor));
+}
+
+Operation *OperationBuilder::getHandleUnwind(int unwindHandlerOffset)
+{
+ return OperationWithPayload<HandleUnwindPayload>::create(m_graphPool, Meta::HandleUnwind,
+ 0, 1, 1, 0, 1, 1,
+ Type(), Operation::NoFlags,
+ HandleUnwindPayload(unwindHandlerOffset));
+}
+
+Operation *OperationBuilder::getFrameState(uint16_t frameSize)
+{
+ if (m_opFrameState == nullptr)
+ m_opFrameState = Operation::create(m_graphPool, Meta::FrameState,
+ frameSize, 0, 0, 0, 0, 1,
+ Type(), Operation::NoFlags);
+ else
+ Q_ASSERT(frameSize == m_opFrameState->valueInputCount());
+
+ return m_opFrameState;
+}
+
+Operation *OperationBuilder::getStart(uint16_t outputCount)
+{
+ return Operation::create(m_graphPool, Meta::Start,
+ 0, 0, 0,
+ outputCount, 1, 1,
+ Type(), Operation::NoFlags);
+}
+
+Operation *OperationBuilder::getEnd(uint16_t controlInputCount)
+{
+ return Operation::create(m_graphPool, Meta::End,
+ 0, 0, controlInputCount,
+ 0, 0, 0,
+ Type(), Operation::NoFlags);
+}
+
+inline Operation *createOperation(Operation::Kind kind, QQmlJS::MemoryPool *staticPool)
+{
+ auto get = [&](uint16_t inValueCount, uint16_t inEffectCount, uint16_t inControlCount,
+ uint16_t outValueCount, uint16_t outEffectCount, uint16_t outControlCount,
+ Type (*typeCreator)(), uint8_t flags) {
+ return Operation::create(staticPool, kind, inValueCount, inEffectCount, inControlCount,
+ outValueCount, outEffectCount, outControlCount, typeCreator(),
+ flags);
+ };
+
+ using K = Operation::Kind;
+ using F = Operation::Flags;
+ const auto none = &Type::noneType;
+ const auto any = &Type::anyType;
+ const auto number = &Type::numberType;
+ const auto boolean = &Type::booleanType;
+
+ switch (kind) {
+ case K::Undefined:
+ return OperationWithPayload<ConstantPayload>::create(
+ staticPool, K::Undefined, 0, 0, 0, 1, 0, 0, Type::undefinedType(), F::NoFlags,
+ ConstantPayload(Primitive::undefinedValue()));
+ case K::Empty:
+ return OperationWithPayload<ConstantPayload>::create(
+ staticPool, K::Constant, 0, 0, 0, 1, 0, 0, Type::emptyType(), Operation::NoFlags,
+ ConstantPayload(Primitive::emptyValue()));
+ case K::Engine: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags);
+ case K::CppFrame: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags);
+ case K::Function: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags);
+ case K::Jump: return get(0, 0, 1, 0, 0, 1, none, F::NoFlags);
+ case K::Return: return get(1, 1, 1, 0, 0, 1, none, F::NoFlags);
+ case K::Branch: return get(1, 0, 1, 0, 0, 2, none, F::HasFrameStateInput | F::NeedsBytecodeOffsets);
+ case K::IfTrue: return get(0, 0, 1, 0, 0, 1, none, F::NoFlags);
+ case K::IfFalse: return get(0, 0, 1, 0, 0, 1, none, F::NoFlags);
+ case K::SelectOutput: return get(3, 1, 1, 1, 1, 1, any, F::NoFlags);
+ case K::Throw: return get(1, 1, 1, 0, 1, 1, any, F::NeedsBytecodeOffsets);
+ case K::OnException: return get(0, 0, 1, 0, 0, 1, none, F::NoFlags);
+ case K::ThrowReferenceError: return get(1, 1, 1, 0, 1, 1, any, F::NeedsBytecodeOffsets);
+ case K::UnwindToLabel: return get(2, 1, 1, 0, 1, 1, none, F::NoFlags);
+ case K::LoadRegExp: return get(1, 0, 0, 1, 0, 0, any, F::NoFlags);
+ case K::ScopedLoad: return get(2, 1, 0, 1, 1, 0, any, F::NoFlags);
+ case K::ScopedStore: return get(3, 1, 0, 0, 1, 0, none, F::NoFlags);
+ case K::JSLoadElement: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSGetLookup: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSLoadProperty: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSStoreElement: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow);
+ case K::JSSetLookupStrict: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow);
+ case K::JSSetLookupSloppy: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow);
+ case K::JSStoreProperty: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow);
+ case K::JSLoadName: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSLoadGlobalLookup: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSStoreNameSloppy: return get(2, 1, 1, 0, 1, 2, none, F::CanThrow);
+ case K::JSStoreNameStrict: return get(2, 1, 1, 0, 1, 2, none, F::CanThrow);
+ case K::JSLoadSuperProperty: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSStoreSuperProperty: return get(2, 1, 1, 0, 1, 2, any, F::CanThrow);
+ case K::JSLoadClosure: return get(1, 1, 0, 1, 1, 0, any, F::Pure);
+ case K::JSGetIterator: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+
+ // special case: see GraphBuilder::generate_IteratorNext
+ case K::JSIteratorNext: return get(2, 1, 1, 2, 1, 1, any, F::NoFlags);
+
+ // special case: see GraphBuilder::generate_IteratorNext
+ case K::JSIteratorNextForYieldStar: return get(3, 1, 1, 2, 1, 1, any, F::NoFlags);
+
+ case K::JSIteratorClose: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSDeleteProperty: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSDeleteName: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSIn: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSInstanceOf: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::QMLLoadQmlContextPropertyLookup: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
+
+ case K::JSEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow);
+ case K::JSGreaterThan: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow);
+ case K::JSGreaterEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow);
+ case K::JSLessThan: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow);
+ case K::JSLessEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow);
+ case K::JSStrictEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow);
+
+ case K::JSAdd: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSSubtract: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow);
+ case K::JSMultiply: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow);
+ case K::JSDivide: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow);
+ case K::JSModulo: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow);
+ case K::JSExponentiate: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow);
+
+ case K::JSBitAnd: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSBitOr: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSBitXor: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSUnsignedShiftRight: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSShiftRight: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSShiftLeft: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+
+ case K::JSNegate: return get(1, 1, 1, 1, 1, 2, number, F::CanThrow);
+ case K::JSToNumber: return get(1, 1, 1, 1, 1, 2, number, F::CanThrow);
+ case K::Alloca: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags);
+
+ //### it is questionable if VAAlloc/VASeal need effect edges
+ case K::VAAlloc: return get(1, 1, 0, 1, 1, 0, none, F::NoFlags);
+
+ case K::VAStore: return get(3, 0, 0, 1, 0, 0, none, F::NoFlags);
+
+ case K::JSTypeofName: return get(1, 1, 0, 1, 1, 0, any, F::NoFlags);
+ case K::JSTypeofValue: return get(1, 0, 0, 1, 0, 0, any, F::Pure);
+ case K::JSDeclareVar: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::JSDestructureRestElement: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
+
+ case K::JSCreateCallContext: return get(0, 1, 1, 0, 1, 1, none, F::NoFlags);
+ case K::JSCreateCatchContext: return get(2, 1, 1, 1, 1, 1, none, F::NoFlags);
+ case K::JSCreateWithContext: return get(1, 1, 1, 1, 1, 1, any, F::NoFlags);
+ case K::JSCreateBlockContext: return get(1, 1, 1, 1, 1, 1, none, F::NoFlags);
+ case K::JSCloneBlockContext: return get(0, 1, 1, 0, 1, 1, none, F::NoFlags);
+ case K::JSCreateScriptContext: return get(1, 1, 1, 1, 1, 1, none, F::NoFlags);
+ case K::JSPopScriptContext: return get(0, 1, 1, 1, 1, 1, none, F::NoFlags);
+ case K::PopContext: return get(0, 1, 1, 0, 1, 1, none, F::NoFlags);
+
+ case K::JSThisToObject: return get(1, 1, 1, 0, 1, 2, any, F::NoFlags);
+ case K::JSCreateMappedArgumentsObject: return get(0, 1, 0, 1, 1, 0, any, F::NoFlags);
+ case K::JSCreateUnmappedArgumentsObject: return get(0, 1, 0, 1, 1, 0, any, F::NoFlags);
+ case K::JSCreateRestParameter: return get(1, 0, 0, 1, 0, 0, any, F::NoFlags);
+ case K::JSLoadSuperConstructor: return get(1, 1, 1, 1, 1, 2, any, F::NoFlags);
+ case K::JSThrowOnNullOrUndefined: return get(1, 1, 1, 0, 1, 2, none, F::CanThrow);
+ case K::JSGetTemplateObject: return get(1, 0, 0, 1, 0, 0, any, F::NoFlags);
+ case K::StoreThis: return get(1, 1, 0, 1, 1, 0, any, F::NoFlags);
+
+ case K::GetException: return get(0, 1, 0, 1, 1, 0, any, F::NoFlags);
+ case K::SetException: return get(1, 1, 0, 0, 1, 0, any, F::NoFlags);
+
+ case K::ToObject: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::ToBoolean: return get(1, 0, 0, 1, 0, 0, boolean, F::Pure);
+
+ case K::IsEmpty: return get(1, 0, 0, 1, 0, 0, boolean, F::Pure);
+
+ case K::BooleanNot: return get(1, 0, 0, 1, 0, 0, boolean, F::NoFlags);
+ case K::HasException: return get(1, 1, 0, 1, 1, 0, boolean, F::NoFlags);
+
+ case K::Swap: return get(0, 0, 0, 0, 0, 0, none, F::NoFlags);
+ case K::Move: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags);
+
+ default: // Non-static operations:
+ return nullptr;
+ }
+}
+
+Operation *OperationBuilder::staticOperation(Operation::Kind kind)
+{
+ static QAtomicPointer<Operation *> ops;
+ if (Operation **staticOps = ops.load())
+ return staticOps[kind];
+
+ static QAtomicInt initializing = 0;
+ if (initializing.testAndSetOrdered(0, 1)) {
+ // This is safe now, because we can only run this piece of code once during the life time
+ // of the application as we can only change initializing from 0 to 1 once.
+ Operation **staticOps = new Operation *[Meta::KindsEnd];
+ static QQmlJS::MemoryPool pool;
+ for (int i = 0; i < Meta::KindsEnd; ++i)
+ staticOps[i] = createOperation(Operation::Kind(i), &pool);
+ bool success = ops.testAndSetOrdered(nullptr, staticOps);
+ Q_ASSERT(success);
+ } else {
+ // Unfortunately we need to busy wait now until the other thread finishes the static
+ // initialization;
+ while (!ops.load()) {}
+ }
+
+ return ops.load()[kind];
+}
+
+Operation *OperationBuilder::getJSVarArgsCall(Operation::Kind kind, uint16_t argc)
+{
+ return Operation::create(m_graphPool, kind,
+ argc, 1, 1, 1, 1, 2,
+ Type::anyType(), Operation::CanThrow);
+}
+
+Operation *OperationBuilder::getJSTailCall(uint16_t argc)
+{
+ return Operation::create(m_graphPool, Meta::JSTailCall,
+ argc, 1, 1, 0, 0, 1,
+ Type(), Operation::NoFlags);
+}
+
+Operation *OperationBuilder::getTailCall()
+{
+ // special varargs call, takes cppframe, engine, func, thisObject, argv, argc
+ return Operation::create(m_graphPool, Meta::TailCall,
+ 6, 1, 1, 0, 0, 1,
+ Type(), Operation::NoFlags);
+}
+
+Operation *OperationBuilder::getCall(Operation::Kind callee)
+{
+ const bool canThrow = CallPayload::canThrow(callee);
+ const Type retTy = CallPayload::returnType(callee);
+ uint16_t nControlInputs = 0;
+ uint16_t nControlOutputs = 0;
+ if (canThrow) {
+ nControlInputs = 1;
+ nControlOutputs += 2;
+ }
+ if (CallPayload::changesContext(callee)) {
+ nControlInputs = 1;
+ nControlOutputs = std::max<uint16_t>(nControlInputs, 1);
+ }
+ if (callee == Meta::Throw || callee == Meta::ThrowReferenceError ||
+ callee == Meta::JSIteratorNext || callee == Meta::JSIteratorNextForYieldStar) {
+ nControlInputs = 1;
+ nControlOutputs = 1;
+ }
+ Operation::Flags flags = Operation::NoFlags;
+ if (canThrow)
+ flags = Operation::Flags(flags | Operation::CanThrow);
+ if (CallPayload::isPure(callee))
+ flags = Operation::Flags(flags | Operation::Pure);
+ const uint16_t nEffects = (flags & Operation::Pure) ? 0 : 1;
+ const uint16_t nValueOutputs = retTy.isNone() ? 0 : 1;
+ const uint16_t nValueInputs = CallPayload::argc(callee);
+
+ return OperationWithPayload<CallPayload>::create(
+ m_graphPool, Meta::Call,
+ nValueInputs, nEffects, nControlInputs,
+ nValueOutputs, nEffects, nControlOutputs,
+ retTy, flags,
+ CallPayload(callee));
+}
+
+Operation *OperationBuilder::getVASeal(uint16_t nElements)
+{
+ return Operation::create(m_graphPool, Meta::VASeal,
+ nElements + 1, 1, 0, 1, 1, 0,
+ Type::anyType(), Operation::NoFlags);
+}
+
+QString Operation::debugString() const
+{
+ switch (kind()) {
+
+ case Meta::Constant:
+ return QStringLiteral("Constant[%1]").arg(ConstantPayload::get(*this)->debugString());
+ case Meta::Parameter:
+ return QStringLiteral("Parameter[%1]").arg(ParameterPayload::get(*this)->debugString());
+ case Meta::Call:
+ return QStringLiteral("Call[%1]").arg(CallPayload::get(*this)->debugString());
+ case Meta::UnwindDispatch:
+ return QStringLiteral("UnwindDispatch[%1]").arg(UnwindDispatchPayload::get(*this)
+ ->debugString());
+ case Meta::HandleUnwind:
+ return QStringLiteral("HandleUnwind[%1]").arg(HandleUnwindPayload::get(*this)
+ ->debugString());
+
+ default:
+ return QString::fromLatin1(QMetaEnum::fromType<Meta::OpKind>().valueToKey(kind()));
+ }
+}
+
+QString ConstantPayload::debugString() const
+{
+ return debugString(m_value);
+}
+
+QString ConstantPayload::debugString(QV4::Value v)
+{
+ if (v.isManaged())
+ return QString::asprintf("Ptr: %p", v.heapObject());
+ if (v.isEmpty())
+ return QStringLiteral("empty");
+ return v.toQStringNoThrow();
+}
+
+QString ParameterPayload::debugString() const
+{
+ return QStringLiteral("%1").arg(m_index);
+}
+
+QString UnwindDispatchPayload::debugString() const
+{
+ return QStringLiteral("%1, %2").arg(QString::number(m_fallthroughSuccessor),
+ QString::number(m_unwindHandlerOffset));
+}
+
+QString HandleUnwindPayload::debugString() const
+{
+ return QStringLiteral("%1").arg(m_unwindHandlerOffset);
+}
+
+static Type translateType(RuntimeSupport::ArgumentType t)
+{
+ switch (t) {
+ case RuntimeSupport::ArgumentType::Int: return Type::int32Type();
+ case RuntimeSupport::ArgumentType::Bool: return Type::booleanType();
+ case RuntimeSupport::ArgumentType::Void: return Type();
+ case RuntimeSupport::ArgumentType::Engine: return Type::rawPointerType();
+ case RuntimeSupport::ArgumentType::ValueRef: return Type::anyType();
+ case RuntimeSupport::ArgumentType::ValueArray: return Type::anyType();
+ case RuntimeSupport::ArgumentType::ReturnedValue: return Type::anyType();
+ default: Q_UNREACHABLE();
+ }
+}
+
+template<template<typename Operation> class M /* MetaOperation */, typename ReturnValue>
+static ReturnValue operateOnRuntimeCall(Operation::Kind kind, bool abortOnMissingCall = true)
+{
+ using K = Operation::Kind;
+ using R = Runtime;
+
+ switch (kind) {
+ case K::Throw: return M<R::ThrowException>::doIt();
+ case K::ThrowReferenceError: return M<R::ThrowReferenceError>::doIt();
+
+ case K::JSEqual: return M<R::CompareEqual>::doIt();
+ case K::JSGreaterThan: return M<R::CompareGreaterThan>::doIt();
+ case K::JSGreaterEqual: return M<R::CompareGreaterEqual>::doIt();
+ case K::JSLessThan: return M<R::CompareLessThan>::doIt();
+ case K::JSLessEqual: return M<R::CompareLessEqual>::doIt();
+ case K::JSStrictEqual: return M<R::CompareStrictEqual>::doIt();
+
+ case K::JSBitAnd: return M<R::BitAnd>::doIt();
+ case K::JSBitOr: return M<R::BitOr>::doIt();
+ case K::JSBitXor: return M<R::BitXor>::doIt();
+ case K::JSUnsignedShiftRight: return M<R::UShr>::doIt();
+ case K::JSShiftRight: return M<R::Shr>::doIt();
+ case K::JSShiftLeft: return M<R::Shl>::doIt();
+
+ case K::JSAdd: return M<R::Add>::doIt();
+ case K::JSSubtract: return M<R::Sub>::doIt();
+ case K::JSMultiply: return M<R::Mul>::doIt();
+ case K::JSDivide: return M<R::Div>::doIt();
+ case K::JSModulo: return M<R::Mod>::doIt();
+ case K::JSExponentiate: return M<R::Exp>::doIt();
+
+ case K::ToBoolean: return M<R::ToBoolean>::doIt();
+ case K::ToObject: return M<R::ToObject>::doIt();
+
+ case K::JSNegate: return M<R::UMinus>::doIt();
+ case K::JSToNumber: return M<R::ToNumber>::doIt();
+
+ case K::JSLoadName: return M<R::LoadName>::doIt();
+ case K::JSLoadElement: return M<R::LoadElement>::doIt();
+ case K::JSStoreElement: return M<R::StoreElement>::doIt();
+ case K::JSGetLookup: return M<R::GetLookup>::doIt();
+ case K::JSSetLookupStrict: return M<R::SetLookupStrict>::doIt();
+ case K::JSSetLookupSloppy: return M<R::SetLookupSloppy>::doIt();
+ case K::JSLoadProperty: return M<R::LoadProperty>::doIt();
+ case K::JSStoreProperty: return M<R::StoreProperty>::doIt();
+ case K::JSLoadGlobalLookup: return M<R::LoadGlobalLookup>::doIt();
+ case K::JSStoreNameSloppy: return M<R::StoreNameSloppy>::doIt();
+ case K::JSStoreNameStrict: return M<R::StoreNameStrict>::doIt();
+ case K::JSLoadSuperProperty: return M<R::LoadSuperProperty>::doIt();
+ case K::JSStoreSuperProperty: return M<R::StoreSuperProperty>::doIt();
+ case K::JSLoadClosure: return M<R::Closure>::doIt();
+ case K::JSGetIterator: return M<R::GetIterator>::doIt();
+ case K::JSIteratorNext: return M<R::IteratorNext>::doIt();
+ case K::JSIteratorNextForYieldStar: return M<R::IteratorNextForYieldStar>::doIt();
+ case K::JSIteratorClose: return M<R::IteratorClose>::doIt();
+ case K::JSDeleteProperty: return M<R::DeleteProperty>::doIt();
+ case K::JSDeleteName: return M<R::DeleteName>::doIt();
+ case K::JSIn: return M<R::In>::doIt();
+ case K::JSInstanceOf: return M<R::Instanceof>::doIt();
+ case K::QMLLoadQmlContextPropertyLookup: return M<R::LoadQmlContextPropertyLookup>::doIt();
+
+ case K::JSTypeofName: return M<R::TypeofName>::doIt();
+ case K::JSTypeofValue: return M<R::TypeofValue>::doIt();
+ case K::JSDeclareVar: return M<R::DeclareVar>::doIt();
+ case K::JSDestructureRestElement: return M<R::DestructureRestElement>::doIt();
+ case K::JSThisToObject: return M<R::ConvertThisToObject>::doIt();
+ case K::JSCreateMappedArgumentsObject: return M<R::CreateMappedArgumentsObject>::doIt();
+ case K::JSCreateUnmappedArgumentsObject: return M<R::CreateUnmappedArgumentsObject>::doIt();
+ case K::JSCreateRestParameter: return M<R::CreateRestParameter>::doIt();
+ case K::JSLoadSuperConstructor: return M<R::LoadSuperConstructor>::doIt();
+ case K::JSThrowOnNullOrUndefined: return M<R::ThrowOnNullOrUndefined>::doIt();
+
+ case K::JSCreateCallContext: return M<R::PushCallContext>::doIt();
+ case K::JSCreateCatchContext: return M<R::PushCatchContext>::doIt();
+ case K::JSCreateWithContext: return M<R::PushWithContext>::doIt();
+ case K::JSCreateBlockContext: return M<R::PushBlockContext>::doIt();
+ case K::JSCloneBlockContext: return M<R::CloneBlockContext>::doIt();
+ case K::JSCreateScriptContext: return M<R::PushScriptContext>::doIt();
+ case K::JSPopScriptContext: return M<R::PopScriptContext>::doIt();
+
+ case K::LoadRegExp: return M<R::RegexpLiteral>::doIt();
+ case K::JSGetTemplateObject: return M<R::GetTemplateObject>::doIt();
+
+ case K::JSCallName: return M<R::CallName>::doIt();
+ case K::JSCallValue: return M<R::CallValue>::doIt();
+ case K::JSCallElement: return M<R::CallElement>::doIt();
+ case K::JSCallLookup: return M<R::CallPropertyLookup>::doIt();
+ case K::JSCallProperty: return M<R::CallProperty>::doIt();
+ case K::JSCallGlobalLookup: return M<R::CallGlobalLookup>::doIt();
+ case K::JSCallPossiblyDirectEval: return M<R::CallPossiblyDirectEval>::doIt();
+ case K::JSCallWithReceiver: return M<R::CallWithReceiver>::doIt();
+ case K::JSDefineObjectLiteral: return M<R::ObjectLiteral>::doIt();
+ case K::JSDefineArray: return M<R::ArrayLiteral>::doIt();
+ case K::JSCallWithSpread: return M<R::CallWithSpread>::doIt();
+ case K::JSConstruct: return M<R::Construct>::doIt();
+ case K::JSConstructWithSpread: return M<R::ConstructWithSpread>::doIt();
+ case K::JSTailCall: return M<R::TailCall>::doIt();
+ case K::JSCreateClass: return M<R::CreateClass>::doIt();
+ default:
+ if (abortOnMissingCall)
+ Q_UNREACHABLE();
+ else
+ return ReturnValue();
+ }
+}
+
+template<typename Method>
+struct IsRuntimeMethodOperation
+{
+ static constexpr bool doIt() { return true; }
+};
+
+bool CallPayload::isRuntimeCall(Operation::Kind m)
+{
+ return operateOnRuntimeCall<IsRuntimeMethodOperation, bool>(m, false);
+}
+
+QString CallPayload::debugString() const
+{
+ return QString::fromLatin1(QMetaEnum::fromType<Meta::OpKind>().valueToKey(m_callee));
+}
+
+template<typename Method>
+struct MethodArgcOperation
+{
+ static constexpr unsigned doIt() { return RuntimeSupport::argumentCount<Method>(); }
+};
+
+unsigned CallPayload::argc(Operation::Kind callee)
+{
+ return operateOnRuntimeCall<MethodArgcOperation, unsigned>(callee);
+}
+
+template<typename Method> struct MethodArg1TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg1Type<Method>(); } };
+template<typename Method> struct MethodArg2TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg2Type<Method>(); } };
+template<typename Method> struct MethodArg3TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg3Type<Method>(); } };
+template<typename Method> struct MethodArg4TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg4Type<Method>(); } };
+template<typename Method> struct MethodArg5TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg5Type<Method>(); } };
+template<typename Method> struct MethodArg6TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg6Type<Method>(); } };
+
+static RuntimeSupport::ArgumentType untranslatedArgumentType(Operation::Kind m, unsigned arg)
+{
+ if (m == Meta::JSTailCall) {
+ if (arg < 4)
+ return RuntimeSupport::ArgumentType::ValueRef;
+ else
+ return RuntimeSupport::ArgumentType::Invalid;
+ }
+
+ switch (arg) {
+ case 0: return operateOnRuntimeCall<MethodArg1TyOperation, RuntimeSupport::ArgumentType>(m);
+ case 1: return operateOnRuntimeCall<MethodArg2TyOperation, RuntimeSupport::ArgumentType>(m);
+ case 2: return operateOnRuntimeCall<MethodArg3TyOperation, RuntimeSupport::ArgumentType>(m);
+ case 3: return operateOnRuntimeCall<MethodArg4TyOperation, RuntimeSupport::ArgumentType>(m);
+ case 4: return operateOnRuntimeCall<MethodArg5TyOperation, RuntimeSupport::ArgumentType>(m);
+ case 5: return operateOnRuntimeCall<MethodArg6TyOperation, RuntimeSupport::ArgumentType>(m);
+ default: return RuntimeSupport::ArgumentType::Invalid;
+ }
+}
+
+bool CallPayload::needsStorageOnJSStack(Operation::Kind m, unsigned arg, const Operation *op,
+ Type nodeType)
+{
+ auto argTy = untranslatedArgumentType(m, arg);
+ if (argTy == RuntimeSupport::ArgumentType::ValueArray)
+ return true;
+ if (argTy != RuntimeSupport::ArgumentType::ValueRef)
+ return false;
+
+ if (op->kind() == Meta::Constant)
+ return true;
+
+ return !nodeType.isObject() && !nodeType.isRawPointer() && !nodeType.isAny();
+}
+
+template<typename Method>
+struct MethodRetTyOperation
+{
+ static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::retType<Method>(); }
+};
+
+Type CallPayload::returnType(Operation::Kind m)
+{
+ if (m == Meta::JSTailCall)
+ return Type();
+
+ auto t = operateOnRuntimeCall<MethodRetTyOperation, RuntimeSupport::ArgumentType>(m);
+ return translateType(t);
+}
+
+static int firstArgumentPositionForType(Operation::Kind m, RuntimeSupport::ArgumentType type)
+{
+ if (operateOnRuntimeCall<MethodArg1TyOperation, RuntimeSupport::ArgumentType>(m) == type)
+ return 1;
+ if (operateOnRuntimeCall<MethodArg2TyOperation, RuntimeSupport::ArgumentType>(m) == type)
+ return 2;
+ if (operateOnRuntimeCall<MethodArg3TyOperation, RuntimeSupport::ArgumentType>(m) == type)
+ return 3;
+ if (operateOnRuntimeCall<MethodArg4TyOperation, RuntimeSupport::ArgumentType>(m) == type)
+ return 4;
+ if (operateOnRuntimeCall<MethodArg5TyOperation, RuntimeSupport::ArgumentType>(m) == type)
+ return 5;
+ if (operateOnRuntimeCall<MethodArg6TyOperation, RuntimeSupport::ArgumentType>(m) == type)
+ return 6;
+ return -1;
+}
+
+unsigned CallPayload::varArgsStart(Operation::Kind m)
+{
+ if (m == Meta::JSTailCall)
+ return 4 - 1;
+
+ int pos = firstArgumentPositionForType(m, RuntimeSupport::ArgumentType::ValueArray) - 1;
+ Q_ASSERT(pos >= 0);
+ return pos;
+}
+
+bool CallPayload::isVarArgsCall(Operation::Kind m)
+{
+ if (m == Meta::JSTailCall)
+ return true;
+ if (lastArgumentIsOutputValue(m))
+ return false;
+ return firstArgumentPositionForType(m, RuntimeSupport::ArgumentType::ValueArray) != -1;
+}
+
+bool CallPayload::isVarArgsCall() const
+{
+ return isVarArgsCall(m_callee);
+}
+
+template<typename Method>
+struct MethodsLastArgumentIsOutputValue
+{
+ static constexpr bool doIt() { return Method::lastArgumentIsOutputValue; }
+};
+
+bool CallPayload::lastArgumentIsOutputValue(Operation::Kind m)
+{
+ return operateOnRuntimeCall<MethodsLastArgumentIsOutputValue, bool>(m);
+}
+
+template<typename Method>
+struct MethodChangesContext
+{
+ static constexpr bool doIt() { return Method::changesContext; }
+};
+
+bool CallPayload::changesContext(Operation::Kind m)
+{
+ return operateOnRuntimeCall<MethodChangesContext, bool>(m);
+}
+
+template<typename Method>
+struct MethodIsPure
+{
+ static constexpr bool doIt() { return Method::pure; }
+};
+
+bool CallPayload::isPure(Operation::Kind m)
+{
+ return operateOnRuntimeCall<MethodIsPure, bool>(m);
+}
+
+template<typename Method>
+struct MethodCanThrow
+{
+ static constexpr bool doIt() { return Method::throws; }
+};
+
+bool CallPayload::canThrow(Operation::Kind m)
+{
+ switch (m) {
+ case Meta::Throw: Q_FALLTHROUGH();
+ case Meta::ThrowReferenceError:
+ // the execution path following these instructions is already linked up to the exception handler
+ return false;
+ case Meta::JSIteratorNext: Q_FALLTHROUGH();
+ case Meta::JSIteratorNextForYieldStar:
+ // special case: see GraphBuilder::generate_IteratorNext
+ return false;
+ default:
+ return operateOnRuntimeCall<MethodCanThrow, bool>(m);
+ }
+}
+
+bool CallPayload::takesEngineAsArg(Operation::Kind m, int arg)
+{
+ return untranslatedArgumentType(m, arg) == RuntimeSupport::ArgumentType::Engine;
+}
+
+bool CallPayload::takesFunctionAsArg(Operation::Kind m, int arg)
+{
+ return untranslatedArgumentType(m, arg) == RuntimeSupport::ArgumentType::Function;
+}
+
+bool CallPayload::takesFrameAsArg(Operation::Kind m, int arg)
+{
+ return untranslatedArgumentType(m, arg) == RuntimeSupport::ArgumentType::Frame;
+}
+
+template<typename Method>
+struct GetMethodPtr
+{
+ static constexpr void *doIt() { return reinterpret_cast<void *>(&Method::call); }
+};
+
+void *CallPayload::getMethodPtr(Operation::Kind m)
+{
+ return operateOnRuntimeCall<GetMethodPtr, void *>(m);
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4operation_p.h b/src/qml/jit/qv4operation_p.h
new file mode 100644
index 0000000000..43214023e8
--- /dev/null
+++ b/src/qml/jit/qv4operation_p.h
@@ -0,0 +1,567 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4OPERATION_P_H
+#define QV4OPERATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4ir_p.h>
+#include <private/qqmljsmemorypool_p.h>
+
+#include <QtCore/qatomic.h>
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+namespace Meta {
+enum OpKind: uint16_t {
+ FrameState,
+ Start,
+ End,
+
+ Undefined,
+ Constant,
+ Parameter,
+ Empty,
+ Engine,
+ CppFrame,
+ Function,
+
+ Jump,
+ Return,
+ JSTailCall,
+ TailCall,
+ Branch,
+ IfTrue,
+ IfFalse,
+ Region,
+ OnException,
+ Phi,
+ EffectPhi,
+ SelectOutput,
+ UnwindDispatch,
+ UnwindToLabel,
+ HandleUnwind,
+ Throw,
+ ThrowReferenceError,
+
+ Call,
+
+ LoadRegExp,
+ ScopedLoad,
+ ScopedStore,
+
+ JSLoadElement,
+ JSStoreElement,
+ JSGetLookup,
+ JSSetLookupStrict,
+ JSSetLookupSloppy,
+ JSLoadProperty,
+ JSStoreProperty,
+ JSLoadName,
+ JSLoadGlobalLookup,
+ JSStoreNameSloppy,
+ JSStoreNameStrict,
+ JSLoadSuperProperty,
+ JSStoreSuperProperty,
+ JSLoadClosure,
+ JSGetIterator,
+ JSIteratorNext,
+ JSIteratorNextForYieldStar,
+ JSIteratorClose,
+ JSDeleteProperty,
+ JSDeleteName,
+ JSIn,
+ JSInstanceOf,
+
+ /* ok, these are qml object ops, but we don't care for now and treat them as JS */
+ QMLLoadQmlContextPropertyLookup,
+ QMLCallQmlContextPropertyLookup,
+
+ JSEqual,
+ JSGreaterThan,
+ JSGreaterEqual,
+ JSLessThan,
+ JSLessEqual,
+ JSStrictEqual,
+
+ JSAdd,
+ JSSubtract,
+ JSMultiply,
+ JSDivide,
+ JSModulo,
+ JSExponentiate,
+
+ JSBitAnd,
+ JSBitOr,
+ JSBitXor,
+ JSUnsignedShiftRight,
+ JSShiftRight,
+ JSShiftLeft,
+
+ JSNegate,
+ JSToNumber,
+
+ JSCallName,
+ JSCallValue,
+ JSCallElement,
+ JSCallProperty,
+ JSCallLookup,
+ JSCallGlobalLookup,
+ JSCallPossiblyDirectEval,
+ JSCallWithReceiver,
+ JSCallWithSpread,
+ JSDefineObjectLiteral,
+ JSDefineArray,
+ JSCreateClass,
+ JSConstruct,
+ JSConstructWithSpread,
+
+ JSTypeofName,
+ JSTypeofValue,
+ JSDeclareVar,
+ JSDestructureRestElement,
+ JSThisToObject,
+ JSCreateMappedArgumentsObject,
+ JSCreateUnmappedArgumentsObject,
+ JSCreateRestParameter,
+ JSLoadSuperConstructor,
+ JSThrowOnNullOrUndefined,
+ JSGetTemplateObject,
+ StoreThis,
+
+ JSCreateCallContext,
+ JSCreateCatchContext,
+ JSCreateWithContext,
+ JSCreateBlockContext,
+ JSCloneBlockContext,
+ JSCreateScriptContext,
+ JSPopScriptContext,
+ PopContext,
+
+ GetException,
+ SetException,
+
+ ToObject,
+ ToBoolean,
+
+ //### do we need this? Or should a later phase generate JumpIsEmpty?
+ IsEmpty,
+
+ Alloca,
+ VAAlloc,
+ VAStore,
+ VASeal,
+
+ BooleanNot,
+ HasException,
+
+ // Low level, used by the register allocator and stack allocator:
+ Swap,
+ Move,
+ KindsEnd
+};
+Q_NAMESPACE
+Q_ENUM_NS(OpKind)
+} // namespace Ops
+
+class Operation
+{
+ Q_DISABLE_COPY_MOVE(Operation)
+
+public:
+ using Kind = Meta::OpKind;
+
+ enum Flags: uint8_t {
+ NoFlags = 0,
+ ThrowsFlag = 1 << 0,
+ Pure = 1 << 1, // no read/write side effect, cannot throw, cannot deopt, and is idempotent
+ NeedsBytecodeOffsets = 1 << 2,
+
+ CanThrow = ThrowsFlag | NeedsBytecodeOffsets,
+
+ HasFrameStateInput = 1 << 3,
+ };
+
+public:
+ static Operation *create(QQmlJS::MemoryPool *pool, Kind kind, uint16_t inValueCount,
+ uint16_t inEffectCount, uint16_t inControlCount,
+ uint16_t outValueCount, uint16_t outEffectCount,
+ uint16_t outControlCount, Type type, uint8_t flags)
+ {
+ return pool->New<Operation>(kind, inValueCount, inEffectCount, inControlCount,
+ outValueCount, outEffectCount, outControlCount,
+ type, Flags(flags));
+ }
+
+ Kind kind() const
+ { return m_kind; }
+
+ bool isConstant() const
+ {
+ switch (kind()) {
+ case Meta::Undefined: Q_FALLTHROUGH();
+ case Meta::Constant:
+ case Meta::Empty:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ QString debugString() const;
+
+ uint16_t valueInputCount() const { return m_inValueCount; }
+ uint16_t effectInputCount() const { return m_inEffectCount; }
+ uint16_t controlInputCount() const { return m_inControlCount; }
+ uint16_t valueOutputCount() const { return m_outValueCount; }
+ uint16_t effectOutputCount() const { return m_outEffectCount; }
+ uint16_t controlOutputCount() const { return m_outControlCount; }
+
+ unsigned indexOfFirstEffect() const { return m_inValueCount; }
+ unsigned indexOfFirstControl() const { return m_inValueCount + m_inEffectCount; }
+ unsigned indexOfFrameStateInput() const
+ {
+ return hasFrameStateInput() ? indexOfFirstControl() + m_inControlCount
+ : std::numeric_limits<unsigned>::max();
+ }
+
+ Type type() const
+ { return m_type; }
+
+ bool canThrow() const
+ { return m_flags & ThrowsFlag; }
+
+ bool isPure() const
+ { return m_flags & Pure; }
+
+ bool needsBytecodeOffsets() const
+ { return m_flags & NeedsBytecodeOffsets; }
+
+ bool hasFrameStateInput() const
+ { return m_flags & HasFrameStateInput; }
+
+ unsigned totalInputCount() const
+ {
+ return valueInputCount() + effectInputCount() + controlInputCount() +
+ (hasFrameStateInput() ? 1 : 0);
+ }
+ unsigned totalOutputCount() const { return valueOutputCount() + effectOutputCount() + controlOutputCount(); }
+
+protected:
+ friend class QQmlJS::MemoryPool;
+ Operation(Kind kind,
+ uint16_t inValueCount, uint16_t inEffectCount, uint16_t inControlCount,
+ uint16_t outValueCount, uint16_t outEffectCount, uint16_t outControlCount,
+ Type type, uint8_t flags)
+ : m_kind(kind)
+ , m_inValueCount(inValueCount)
+ , m_inEffectCount(inEffectCount)
+ , m_inControlCount(inControlCount)
+ , m_outValueCount(outValueCount)
+ , m_outEffectCount(outEffectCount)
+ , m_outControlCount(outControlCount)
+ , m_type(type)
+ , m_flags(Flags(flags))
+ {
+ }
+
+ ~Operation() = default;
+
+private:
+ Kind m_kind;
+ uint16_t m_inValueCount;
+ uint16_t m_inEffectCount;
+ uint16_t m_inControlCount;
+ uint16_t m_outValueCount;
+ uint16_t m_outEffectCount;
+ uint16_t m_outControlCount;
+ Type m_type;
+ Flags m_flags;
+};
+
+template <typename Payload>
+class OperationWithPayload: public Operation
+{
+public:
+ static OperationWithPayload *create(QQmlJS::MemoryPool *pool, Kind kind,
+ uint16_t inValueCount, uint16_t inEffectCount, uint16_t inControlCount,
+ uint16_t outValueCount, uint16_t outEffectCount, uint16_t outControlCount,
+ Type type, Flags flags, Payload payload)
+ {
+ return pool->New<OperationWithPayload>(kind, inValueCount, inEffectCount, inControlCount,
+ outValueCount, outEffectCount, outControlCount,
+ type, flags, payload);
+ }
+
+ const Payload &payload() const
+ { return m_payload; }
+
+protected:
+ friend class QQmlJS::MemoryPool;
+ OperationWithPayload(Kind kind,
+ uint16_t inValueCount, uint16_t inEffectCount, uint16_t inControlCount,
+ uint16_t outValueCount, uint16_t outEffectCount, uint16_t outControlCount,
+ Type type, Flags flags, Payload payload)
+ : Operation(kind,
+ inValueCount, inEffectCount, inControlCount,
+ outValueCount, outEffectCount, outControlCount,
+ type, flags)
+ , m_payload(payload)
+ {}
+
+ ~OperationWithPayload() = default;
+
+private:
+ Payload m_payload;
+};
+
+class ConstantPayload
+{
+public:
+ explicit ConstantPayload(QV4::Value v)
+ : m_value(v)
+ {}
+
+ QV4::Value value() const
+ { return m_value; }
+
+ static const ConstantPayload *get(const Operation &op)
+ {
+ if (op.kind() != Meta::Constant)
+ return nullptr;
+
+ return &static_cast<const OperationWithPayload<ConstantPayload>&>(op).payload();
+ }
+
+ QString debugString() const;
+ static QString debugString(QV4::Value v);
+
+private:
+ QV4::Value m_value;
+};
+
+class ParameterPayload
+{
+public:
+ ParameterPayload(size_t index, Function::StringId stringId)
+ : m_index(index)
+ , m_stringId(stringId)
+ {}
+
+ size_t parameterIndex() const
+ { return m_index; }
+
+ Function::StringId stringId() const
+ { return m_stringId; }
+
+ static const ParameterPayload *get(const Operation &op)
+ {
+ if (op.kind() != Meta::Parameter)
+ return nullptr;
+
+ return &static_cast<const OperationWithPayload<ParameterPayload>&>(op).payload();
+ }
+
+ QString debugString() const;
+
+private:
+ size_t m_index;
+ Function::StringId m_stringId;
+};
+
+class CallPayload
+{
+public:
+ CallPayload(Operation::Kind callee)
+ : m_callee(callee)
+ {}
+
+ static const CallPayload *get(const Operation &op)
+ {
+ if (op.kind() != Meta::Call)
+ return nullptr;
+
+ return &static_cast<const OperationWithPayload<CallPayload>&>(op).payload();
+ }
+
+ static bool isRuntimeCall(Operation::Kind m);
+
+ Operation::Kind callee() const { return m_callee; }
+ QString debugString() const;
+
+ unsigned argc() const { return argc(m_callee); }
+ static unsigned argc(Operation::Kind callee);
+ static bool needsStorageOnJSStack(Operation::Kind m, unsigned arg, const Operation *op,
+ Type nodeType);
+ static Type returnType(Operation::Kind m);
+ static int engineArgumentPosition(Operation::Kind m);
+ static int functionArgumentPosition(Operation::Kind m);
+
+ static constexpr unsigned NoVarArgs = std::numeric_limits<unsigned>::max();
+ static unsigned varArgsStart(Operation::Kind m);
+ static bool isVarArgsCall(Operation::Kind m);
+ bool isVarArgsCall() const;
+ static bool lastArgumentIsOutputValue(Operation::Kind m);
+ static bool changesContext(Operation::Kind m);
+ static bool isPure(Operation::Kind m);
+ static bool canThrow(Operation::Kind m);
+ static bool takesEngineAsArg(Operation::Kind m, int arg);
+ static bool takesFunctionAsArg(Operation::Kind m, int arg);
+ static bool takesFrameAsArg(Operation::Kind m, int arg);
+ static void *getMethodPtr(Operation::Kind m);
+
+private:
+ Operation::Kind m_callee;
+};
+
+class UnwindDispatchPayload
+{
+public:
+ UnwindDispatchPayload(int unwindHandlerOffset, int fallthroughSuccessor)
+ : m_unwindHandlerOffset(unwindHandlerOffset)
+ , m_fallthroughSuccessor(fallthroughSuccessor)
+ {}
+
+ int unwindHandlerOffset() const
+ { return m_unwindHandlerOffset; }
+
+ int fallthroughSuccessor() const //### unused...
+ { return m_fallthroughSuccessor; }
+
+ static const UnwindDispatchPayload *get(const Operation &op)
+ {
+ if (op.kind() != Meta::UnwindDispatch)
+ return nullptr;
+
+ return &static_cast<const OperationWithPayload<UnwindDispatchPayload>&>(op).payload();
+ }
+
+ QString debugString() const;
+
+private:
+ int m_unwindHandlerOffset;
+ int m_fallthroughSuccessor;
+};
+
+class HandleUnwindPayload
+{
+public:
+ HandleUnwindPayload(int unwindHandlerOffset)
+ : m_unwindHandlerOffset(unwindHandlerOffset)
+ {}
+
+ int unwindHandlerOffset() const
+ { return m_unwindHandlerOffset; }
+
+ static const HandleUnwindPayload *get(const Operation &op)
+ {
+ if (op.kind() != Meta::HandleUnwind)
+ return nullptr;
+
+ return &static_cast<const OperationWithPayload<HandleUnwindPayload>&>(op).payload();
+ }
+
+ QString debugString() const;
+
+private:
+ int m_unwindHandlerOffset;
+};
+
+class OperationBuilder
+{
+ Q_DISABLE_COPY_MOVE(OperationBuilder)
+
+ friend class QQmlJS::MemoryPool;
+ OperationBuilder(QQmlJS::MemoryPool *graphPool);
+
+public:
+ static OperationBuilder *create(QQmlJS::MemoryPool *pool);
+ ~OperationBuilder() = delete;
+
+ Operation *getConstant(QV4::Value v);
+ Operation *getFrameState(uint16_t frameSize);
+ Operation *getStart(uint16_t outputCount);
+ Operation *getEnd(uint16_t controlInputCount);
+ Operation *getParam(unsigned index, Function::StringId name);
+ Operation *getRegion(unsigned nControlInputs);
+ Operation *getPhi(unsigned nValueInputs);
+ Operation *getEffectPhi(unsigned nEffectInputs);
+ Operation *getUnwindDispatch(unsigned nControlOutputs, int unwindHandlerOffset, int fallthroughSuccessor);
+ Operation *getHandleUnwind(int unwindHandlerOffset);
+
+ template<Operation::Kind kind>
+ Operation *get() {
+ return staticOperation(kind);
+ }
+
+ Operation *getVASeal(uint16_t nElements);
+
+ Operation *getJSVarArgsCall(Operation::Kind kind, uint16_t argc);
+ Operation *getJSTailCall(uint16_t argc);
+ Operation *getTailCall();
+
+ Operation *getCall(Operation::Kind callee);
+
+private:
+ QQmlJS::MemoryPool *m_graphPool; // used to store per-graph nodes
+ Operation *m_opFrameState = nullptr;
+ static Operation *staticOperation(Operation::Kind kind);
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4OPERATION_P_H
diff --git a/src/qml/jit/qv4runtimesupport_p.h b/src/qml/jit/qv4runtimesupport_p.h
new file mode 100644
index 0000000000..0dc6022331
--- /dev/null
+++ b/src/qml/jit/qv4runtimesupport_p.h
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4RUNTIMESUPPORT_P_H
+#define QV4RUNTIMESUPPORT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qv4runtimeapi_p.h>
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+namespace RuntimeSupport {
+
+template <typename T>
+struct CountArguments {
+ static constexpr unsigned count = 0;
+};
+template <typename RetTy, typename... Args>
+struct CountArguments<RetTy (*)(Args... args)> {
+ static constexpr unsigned count = sizeof...(Args) ;
+};
+
+template<typename M>
+static constexpr unsigned argumentCount() {
+ using type = decltype(&M::call);
+ return CountArguments<type>::count;
+}
+
+enum class ArgumentType {
+ Invalid,
+ Engine,
+ Frame,
+ Function,
+ ValueRef,
+ ValueArray,
+ ReturnedValue,
+ Int,
+ Bool,
+ Void,
+};
+
+
+template <typename T>
+struct JavaScriptType
+{
+ // No default type. We want to make sure everything we do is actually recognized.
+};
+
+template <typename T>
+struct ReturnValue
+{
+ // No default type.
+};
+
+template <int I, typename T>
+struct Argument
+{
+ // For simplicity, we add a default here. Otherwise we would need to spell out more
+ // combinations of I and number of arguments of T.
+ static constexpr ArgumentType type = ArgumentType::Invalid;
+};
+
+template <typename RetTy, typename T, typename... Args>
+struct Argument<1, RetTy (*)(T, Args... args)> {
+ static constexpr ArgumentType type = JavaScriptType<T>::type;
+};
+
+template <typename RetTy, typename Arg1, typename T, typename... Args>
+struct Argument<2, RetTy (*)(Arg1, T, Args... args)> {
+ static constexpr ArgumentType type = JavaScriptType<T>::type;
+};
+
+template <typename RetTy, typename Arg1, typename Arg2, typename T,
+ typename... Args>
+struct Argument<3, RetTy (*)(Arg1, Arg2, T, Args... args)> {
+ static constexpr ArgumentType type = JavaScriptType<T>::type;
+};
+
+template <typename RetTy, typename Arg1, typename Arg2,
+ typename Arg3, typename T, typename... Args>
+struct Argument<4, RetTy (*)(Arg1, Arg2, Arg3, T, Args... args)> {
+ static constexpr ArgumentType type = JavaScriptType<T>::type;
+};
+
+template <typename RetTy, typename Arg1, typename Arg2,
+ typename Arg3, typename Arg4, typename T, typename... Args>
+struct Argument<5, RetTy (*)(Arg1, Arg2, Arg3, Arg4, T, Args... args)> {
+ static constexpr ArgumentType type = JavaScriptType<T>::type;
+};
+
+template <typename RetTy, typename Arg1, typename Arg2,
+ typename Arg3, typename Arg4, typename Arg5, typename T, typename... Args>
+struct Argument<6, RetTy (*)(Arg1, Arg2, Arg3, Arg4, Arg5, T, Args... args)> {
+ static constexpr ArgumentType type = JavaScriptType<T>::type;
+};
+
+template <typename RetTy, typename... Args>
+struct ReturnValue<RetTy (*)(Args... args)> {
+ static constexpr ArgumentType type = JavaScriptType<RetTy>::type;
+};
+
+template<>
+struct JavaScriptType<QV4::ExecutionEngine *>
+{
+ static constexpr ArgumentType type = ArgumentType::Engine;
+};
+
+template<>
+struct JavaScriptType<QV4::CppStackFrame *>
+{
+ static constexpr ArgumentType type = ArgumentType::Frame;
+};
+
+template<>
+struct JavaScriptType<QV4::Function *>
+{
+ static constexpr ArgumentType type = ArgumentType::Function;
+};
+
+template<>
+struct JavaScriptType<const QV4::Value &>
+{
+ static constexpr ArgumentType type = ArgumentType::ValueRef;
+};
+
+template<>
+// We need to pass Value * in order to match a parmeter Value[].
+struct JavaScriptType<QV4::Value *>
+{
+ static constexpr ArgumentType type = ArgumentType::ValueArray;
+};
+
+template<>
+struct JavaScriptType<int>
+{
+ static constexpr ArgumentType type = ArgumentType::Int;
+};
+
+template<>
+struct JavaScriptType<QV4::Bool>
+{
+ static constexpr ArgumentType type = ArgumentType::Bool;
+};
+
+template<>
+struct JavaScriptType<QV4::ReturnedValue>
+{
+ static constexpr ArgumentType type = ArgumentType::ReturnedValue;
+};
+
+template<>
+struct JavaScriptType<void>
+{
+ static constexpr ArgumentType type = ArgumentType::Void;
+};
+
+template<typename M>
+static constexpr ArgumentType retType() {
+ using Type = decltype(&M::call);
+ return ReturnValue<Type>::type;
+}
+
+template<typename M>
+static constexpr ArgumentType arg1Type() {
+ using Type = decltype(&M::call);
+ return Argument<1, Type>::type;
+}
+
+template<typename M>
+static constexpr ArgumentType arg2Type() {
+ using Type = decltype(&M::call);
+ return Argument<2, Type>::type;
+}
+
+template<typename M>
+static constexpr ArgumentType arg3Type() {
+ using Type = decltype(&M::call);
+ return Argument<3, Type>::type;
+}
+
+template<typename M>
+static constexpr ArgumentType arg4Type() {
+ using Type = decltype(&M::call);
+ return Argument<4, Type>::type;
+}
+
+template<typename M>
+static constexpr ArgumentType arg5Type() {
+ using Type = decltype(&M::call);
+ return Argument<5, Type>::type;
+}
+
+template<typename M>
+static constexpr ArgumentType arg6Type() {
+ using Type = decltype(&M::call);
+ return Argument<6, Type>::type;
+}
+
+} // namespace RuntimeSupport
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4RUNTIMESUPPORT_P_H
diff --git a/src/qml/jit/qv4schedulers.cpp b/src/qml/jit/qv4schedulers.cpp
new file mode 100644
index 0000000000..0dffefa951
--- /dev/null
+++ b/src/qml/jit/qv4schedulers.cpp
@@ -0,0 +1,912 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qloggingcategory.h>
+
+#include "qv4schedulers_p.h"
+#include "qv4util_p.h"
+#include "qv4graph_p.h"
+#include "qv4blockscheduler_p.h"
+#include "qv4stackframe_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcSched, "qt.v4.ir.scheduling")
+Q_LOGGING_CATEGORY(lcDotCFG, "qt.v4.ir.scheduling.cfg")
+
+static bool needsScheduling(Node *n)
+{
+ if (n->operation()->isConstant())
+ return false;
+ switch (n->opcode()) {
+ case Meta::Function: Q_FALLTHROUGH();
+ case Meta::CppFrame:
+ case Meta::Phi:
+ case Meta::EffectPhi:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool NodeScheduler::canStartBlock(Node *node) const
+{
+ switch (node->operation()->kind()) {
+ case Meta::Start: Q_FALLTHROUGH();
+ case Meta::IfTrue:
+ case Meta::IfFalse:
+ case Meta::Region:
+ case Meta::HandleUnwind:
+ case Meta::OnException:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool NodeScheduler::isControlFlowSplit(Node *node) const
+{
+ int nOutputs = node->operation()->controlOutputCount();
+ if (nOutputs == 2) {
+ // if there is a "missing" control output, it's for exception flow without unwinder
+ int controlUses = 0;
+ auto uses = node->uses();
+ for (auto it = uses.begin(), eit = uses.end(); it != eit; ++it) {
+ if (isLive(*it) && it.isUsedAsControl())
+ ++controlUses;
+ }
+ return controlUses == 2;
+ }
+ return nOutputs > 2;
+}
+
+bool NodeScheduler::isBlockTerminator(Node *node) const
+{
+ switch (node->operation()->kind()) {
+ case Meta::Branch: Q_FALLTHROUGH();
+ case Meta::Jump:
+ case Meta::Return:
+ case Meta::TailCall:
+ case Meta::UnwindDispatch:
+ case Meta::End:
+ return true;
+ case Meta::Call:
+ return isControlFlowSplit(node);
+ default:
+ return false;
+ }
+}
+
+MIBlock *NodeScheduler::getCommonDominator(MIBlock *one, MIBlock *other) const
+{
+ MIBlock::Index a = one->index();
+ MIBlock::Index b = other->index();
+
+ while (a != b) {
+ if (m_dominatorDepthForBlock[a] < m_dominatorDepthForBlock[b])
+ b = m_domTree->immediateDominator(b);
+ else
+ a = m_domTree->immediateDominator(a);
+ }
+
+ return m_miFunction->block(a);
+}
+
+// For Nodes that end up inside loops, it'd be great if we can move (hoist) them out of the loop.
+// To do that, we need a block that preceeds the loop. (So the block before the loop header.)
+// This function calculates that hoist block if the original block is in a loop.
+MIBlock *NodeScheduler::getHoistBlock(MIBlock *block) const
+{
+ if (m_loopInfo->isLoopHeader(block))
+ return m_miFunction->block(m_domTree->immediateDominator(block->index()));
+
+ // make the loop header a candidate:
+ MIBlock *loopHeader = m_loopInfo->loopHeaderFor(block);
+ if (loopHeader == nullptr)
+ return nullptr; // block is not in a loop
+
+ // And now the tricky part: block has to dominate all exits from the loop. If it does not do
+ // that, it meanse that there is an exit from the loop that can be reached before block. In
+ // that case, hoisting from "block" to "loopHeader" would mean there now is an extra calculation
+ // that is not needed for a certain loop exit.
+ for (MIBlock *outEdge : m_loopInfo->loopExitsForLoop(loopHeader)) {
+ if (getCommonDominator(block, outEdge) != block)
+ return nullptr;
+ }
+
+ return m_miFunction->block(m_domTree->immediateDominator(loopHeader->index()));
+}
+
+NodeScheduler::NodeScheduler(Function *irFunction)
+ : m_irFunction(irFunction)
+ , m_vregs(irFunction->graph()->nodeCount(), std::numeric_limits<unsigned>::max())
+ , m_live(irFunction->graph(), /*collectUses =*/ false /* do explicitly NOT collect uses! */)
+{
+}
+
+MIFunction *NodeScheduler::buildMIFunction()
+{
+ m_miFunction = new MIFunction(m_irFunction);
+
+ // step 1: build the CFG
+ auto roots = buildCFG();
+ m_miFunction->renumberBlocks();
+ m_miFunction->dump(QStringLiteral("CFG after renumbering"));
+
+ Q_ASSERT(m_miFunction->block(MIFunction::StartBlockIndex)->index()
+ == MIFunction::StartBlockIndex);
+ Q_ASSERT(m_miFunction->block(MIFunction::StartBlockIndex)->instructions().front().opcode()
+ == Meta::Start);
+
+ // step 2: build the dominator tree
+ if (lcDotCFG().isDebugEnabled())
+ dumpDotCFG();
+ m_domTree.reset(new DominatorTree(m_miFunction));
+ m_dominatorDepthForBlock = m_domTree->calculateNodeDepths();
+
+ // step 3: find loops
+ m_loopInfo.reset(new LoopInfo(*m_domTree.data()));
+ m_loopInfo->detectLoops();
+
+ // step 4: schedule early
+ scheduleEarly(roots);
+ showNodesByBlock(QStringLiteral("nodes per block after early scheduling"));
+
+ // step 5: schedule late
+ scheduleLate(roots);
+ showNodesByBlock(QStringLiteral("nodes per block after late scheduling"));
+
+ // step 6: schedule instructions in each block
+ scheduleNodesInBlock();
+
+ m_miFunction->dump(QStringLiteral("MI before block scheduling"));
+
+ // step 7: order the basic blocks in the CFG
+ BlockScheduler blockScheduler(*m_domTree.data(), *m_loopInfo.data());
+ m_miFunction->setBlockOrder(blockScheduler.scheduledBlockSequence());
+
+ // we're done
+ m_miFunction->renumberInstructions();
+ m_miFunction->setVregCount(m_nextVReg);
+ m_miFunction->dump(QStringLiteral("MI after scheduling"));
+ return m_miFunction;
+}
+
+static Node *splitEdge(Function *irFunction, Node *node, unsigned inputIndex)
+{
+ Graph *g = irFunction->graph();
+ Node *in = node->input(inputIndex);
+ Node *region = g->createNode(g->opBuilder()->getRegion(1), &in, 1);
+ Node *jump = g->createNode(g->opBuilder()->get<Meta::Jump>(), &region, 1);
+
+ qCDebug(lcSched) << "splitting critical edge from node" << node->id()
+ << "to node" << node->input(inputIndex)->id()
+ << "by inserting jump node" << jump->id()
+ << "and region node" << region->id();
+
+ node->replaceInput(inputIndex, jump);
+ return jump;
+}
+
+// See Chapter 6.3.1 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for
+// a description of the algorithm.
+std::vector<Node *> NodeScheduler::buildCFG()
+{
+ std::vector<Node *> roots;
+ roots.reserve(32);
+ NodeWorkList todo(m_irFunction->graph());
+
+ auto enqueueControlInputs = [this, &todo](Node *node) {
+ for (unsigned i = 0, ei = node->operation()->controlInputCount(); i != ei; ++i) {
+ const auto inputIndex = node->operation()->indexOfFirstControl() + i;
+ Node *input = node->input(inputIndex);
+ Q_ASSERT(input);
+ if (node->operation()->kind() == Meta::Region
+ && node->operation()->controlInputCount() > 1
+ && isControlFlowSplit(input)) {
+ // critical edge!
+ input = splitEdge(m_irFunction, node, inputIndex);
+ m_live.markReachable(input);
+ m_live.markReachable(input->controlInput(0));
+ }
+ if (!isBlockTerminator(input)) {
+ auto g = m_irFunction->graph();
+ Node *jump = g->createNode(g->opBuilder()->get<Meta::Jump>(), &input, 1);
+ node->replaceInput(inputIndex, jump);
+ m_live.markReachable(jump);
+ qCDebug(lcSched) << "inserting jump node" << jump->id()
+ << "between node" << node->id()
+ << "and node" << input->id();
+ input = jump;
+ }
+ todo.enqueue(input);
+ }
+ };
+
+ // create the CFG by scheduling control dependencies that start/end blocks:
+ todo.enqueue(m_irFunction->graph()->endNode());
+ while (Node *node = todo.dequeueNextNodeForVisiting()) {
+ Q_ASSERT(isBlockTerminator(node));
+
+ if (schedulerData(node)->minimumBlock)
+ continue;
+
+ MIBlock *b = m_miFunction->addBlock();
+
+ qCDebug(lcSched) << "scheduling node" << node->id() << "as terminator for new block"
+ << b->index();
+ b->instructions().push_front(createMIInstruction(node));
+ placeFixed(node, b, Schedule);
+ roots.push_back(node);
+
+ if (Node *framestate = node->frameStateInput()) {
+ placeFixed(framestate, b, DontSchedule);
+ qCDebug(lcSched) << ".. also scheduling framestate dependency node" << node->id()
+ << "in block" << b->index();
+ }
+
+ if (node->opcode() == Meta::End) {
+ enqueueControlInputs(node);
+ continue;
+ }
+
+ while (true) {
+ Node *controlDependency = node->controlInput(0);
+ if (!controlDependency)
+ break;
+ if (todo.isVisited(controlDependency))
+ break;
+ if (schedulerData(controlDependency)->isFixed)
+ break;
+
+ if (controlDependency->opcode() == Meta::Start) {
+ qCDebug(lcSched) << "placing start node" << controlDependency->id()
+ << "in block" << b->index();
+ handleStartNode(controlDependency, b);
+ placeFixed(controlDependency, b, Schedule);
+ roots.push_back(controlDependency);
+ break; // we're done with this block
+ }
+ if (isBlockTerminator(controlDependency)) {
+ qCDebug(lcSched) << "found terminator node" << controlDependency->id()
+ << "for another block, so finish block" << b->index();
+ Node *merge = m_irFunction->graph()->createNode(
+ m_irFunction->graph()->opBuilder()->getRegion(1), &controlDependency, 1);
+ node->replaceInput(node->operation()->indexOfFirstControl(), merge);
+ addBlockStart(roots, merge, b);
+ placeFixed(merge, b, Schedule);
+ m_live.markReachable(merge);
+ todo.enqueue(controlDependency);
+ break; // we're done with this block
+ }
+ if (canStartBlock(controlDependency)
+ || schedulerData(controlDependency->controlInput())->isFixed) {
+ qCDebug(lcSched) << "found block start node" << controlDependency->id()
+ << "for this block, so finish block" << b->index();
+ addBlockStart(roots, controlDependency, b);
+ placeFixed(controlDependency, b, Schedule);
+ roots.push_back(controlDependency);
+ enqueueControlInputs(controlDependency);
+ break; // we're done with this block
+ }
+ qCDebug(lcSched) << "skipping node" << controlDependency->id();
+ node = controlDependency;
+ }
+ }
+
+ // link the edges of the MIBlocks, and add basic-block arguments:
+ for (MIBlock *toBlock : m_miFunction->blocks()) {
+ Q_ASSERT(!toBlock->instructions().empty());
+ MIInstr &instr = toBlock->instructions().front();
+ Node *toNode = instr.irNode();
+ const auto opcode = toNode->operation()->kind();
+ if (opcode == Meta::Region) {
+ unsigned inputNr = 0;
+ for (Node *input : toNode->inputs()) {
+ MIBlock *fromBlock = schedulerData(input)->minimumBlock;
+ fromBlock->addOutEdge(toBlock);
+ toBlock->addInEdge(fromBlock);
+ MIInstr &fromTerminator = fromBlock->instructions().back();
+ if (fromTerminator.irNode()->opcode() == Meta::Jump ||
+ fromTerminator.irNode()->opcode() == Meta::UnwindDispatch) {
+ unsigned arg = 0;
+ for (const MIOperand &bbArg : toBlock->arguments()) {
+ fromTerminator.setOperand(arg++,
+ createMIOperand(bbArg.irNode()->input(inputNr)));
+ }
+ }
+ ++inputNr;
+ }
+ } else if (opcode == Meta::End) {
+ for (Node *input : toNode->inputs()) {
+ MIBlock *fromBlock = schedulerData(input)->minimumBlock;
+ fromBlock->addOutEdge(toBlock);
+ toBlock->addInEdge(fromBlock);
+ }
+ } else if (Node *fromNode = toNode->controlInput()) {
+ MIBlock *fromBlock = schedulerData(fromNode)->minimumBlock;
+ fromBlock->addOutEdge(toBlock);
+ toBlock->addInEdge(fromBlock);
+ }
+ }
+
+ m_irFunction->dump(QStringLiteral("graph after building CFG"));
+
+ auto startBlock = schedulerData(m_irFunction->graph()->startNode())->minimumBlock;
+ m_miFunction->setStartBlock(startBlock);
+
+ if (lcSched().isDebugEnabled())
+ m_miFunction->dump(QStringLiteral("control flow graph before renumbering"));
+ m_miFunction->verifyCFG();
+
+ return roots;
+}
+
+// See Chapter 6.3.3 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for
+// a description of the algorithm.
+void NodeScheduler::scheduleEarly(const std::vector<Node *> &roots)
+{
+ // scheduling one node might have the effect of queueing its dependencies
+ NodeWorkList todo(m_irFunction->graph());
+ for (Node *root : roots) {
+ todo.enqueue(root);
+ while (Node *node = todo.dequeueNextNodeForVisiting())
+ scheduleEarly(node, todo);
+ }
+}
+
+void NodeScheduler::scheduleEarly(Node *node, NodeWorkList &todo)
+{
+ qCDebug(lcSched) << "Scheduling node" << node->id() << "early...";
+
+ SchedulerData *sd = schedulerData(node);
+
+ if (sd->isFixed) {
+ // Fixed nodes already know their schedule early position.
+ qCDebug(lcSched) << ".. Fixed node" << node->id() << "is on minimum block"
+ << sd->minimumBlock->index()
+ << "which has dominator depth"
+ << m_dominatorDepthForBlock[sd->minimumBlock->index()];
+ }
+
+ for (Node *use : node->uses()) {
+ if (isLive(use))
+ propagateMinimumPosition(sd->minimumBlock, use, todo);
+ else
+ qCDebug(lcSched) << ".. Skipping node" << use->id() << "as it's not live";
+ }
+}
+
+void NodeScheduler::propagateMinimumPosition(MIBlock *newMinimumPosition, Node *toNode,
+ NodeWorkList &todo)
+{
+ Q_ASSERT(newMinimumPosition);
+
+ SchedulerData *sd = schedulerData(toNode);
+ if (sd->isFixed) // nothing to do
+ return;
+
+ MIBlock::Index minimumBlockIndex = sd->minimumBlock
+ ? sd->minimumBlock->index()
+ : MIFunction::StartBlockIndex;
+ Q_ASSERT(m_domTree->insideSameDominatorChain(newMinimumPosition->index(), minimumBlockIndex));
+ if (sd->minimumBlock == nullptr
+ || m_dominatorDepthForBlock[newMinimumPosition->index()]
+ > m_dominatorDepthForBlock[minimumBlockIndex]) {
+ // ok, some input for toNode is scheduled *after* our current minimum depth, so we need
+ // to adjust out minimal position. (This might involve rescheduling toNode's uses.)
+ place(toNode, newMinimumPosition);
+ todo.reEnqueue(toNode);
+ qCDebug(lcSched) << ".. Propagating minimum block" << sd->minimumBlock->index()
+ << "which has dominator depth"
+ << m_dominatorDepthForBlock[newMinimumPosition->index()]
+ << "to use node" << toNode->id();
+ } else {
+ qCDebug(lcSched) << ".. Minimum position" << newMinimumPosition->index()
+ << "is not better than" << minimumBlockIndex
+ << "for node" << toNode->id();
+ }
+}
+
+// See Chapter 6.3.4 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for
+// a description of the algorithm.
+//
+// There is one extra detail not described in the thesis mentioned above: loop hoisting. Before we
+// place a node in the latest block that dominates all uses, we check if we accidentally sink it
+// *into* a loop (meaning the latest block is inside a loop, where it is not if the earliest
+// possible block would be chosen). If we detect that a nodes is going to sink into a loop, we walk
+// the dominator path from the latest block up to the earliest block, and pick the first block that
+// is in the same loop (if any) as the earlieast block.
+//
+// As noted in the thesis, this strategy might enlongen life times, which could be harmful for
+// values that are simple to re-materialized or re-calculate.
+void NodeScheduler::scheduleLate(const std::vector<Node *> &roots)
+{
+ NodeWorkList todo(m_irFunction->graph());
+ for (Node *root : roots)
+ todo.enqueue(root);
+
+ while (Node *node = todo.dequeueNextNodeForVisiting())
+ scheduleNodeLate(node, todo);
+}
+
+void NodeScheduler::scheduleNodeLate(Node *node, NodeWorkList &todo)
+{
+ if (!needsScheduling(node))
+ return;
+ qCDebug(lcSched) << "Scheduling node" << node->id() << "late...";
+
+ auto sd = schedulerData(node);
+ if (sd->unscheduledUses == SchedulerData::NotYetCalculated) {
+ sd->unscheduledUses = 0;
+ for (Node *use : node->uses()) {
+ if (!isLive(use))
+ continue;
+ if (!needsScheduling(use))
+ continue;
+ if (schedulerData(use)->isFixed)
+ continue;
+ todo.enqueue(use);
+ ++sd->unscheduledUses;
+ }
+ }
+
+ if (sd->isFixed) {
+ qCDebug(lcSched) << ".. it's fixed";
+ enqueueInputs(node, todo);
+ return;
+ }
+
+ if (sd->unscheduledUses) {
+ qCDebug(lcSched).noquote() << ".. not all uses are fixed, postpone it."<< todo.status(node);
+ return;
+ }
+
+ MIBlock *&minBlock = sd->minimumBlock;
+ if (minBlock == nullptr)
+ minBlock = m_miFunction->block(MIFunction::StartBlockIndex);
+ MIBlock *commonUseDominator = commonDominatorOfUses(node);
+ qCDebug(lcSched) << ".. common use dominator: block" << commonUseDominator->index();
+
+ // the minBlock has to dominate the uses, *and* the common dominator of the uses.
+ Q_ASSERT(minBlock->index() == commonUseDominator->index() ||
+ m_domTree->dominates(minBlock->index(), commonUseDominator->index()));
+
+ // we now found the deepest block, so use it as the target block:
+ MIBlock *targetBlock = commonUseDominator;
+
+ if (node->opcode() == Meta::FrameState) {
+ // never hoist framestates: they're used (among other things) to keep their inputs alive, so
+ // hoisting them out would end the life-time of those inputs prematurely
+ } else {
+ // but we want to prevent blocks sinking into loops unnecessary
+ MIBlock *hoistBlock = getHoistBlock(targetBlock);
+ while (hoistBlock
+ && m_dominatorDepthForBlock[hoistBlock->index()]
+ >= m_dominatorDepthForBlock[minBlock->index()]) {
+ qCDebug(lcSched) << ".. hoisting node" << node->id() << "from block"
+ << targetBlock->index() << "to block" << hoistBlock->index();
+ // ok, so there a) is a hoist block and b) it's deeper than the minimum block,
+ // so lift it up one level ...
+ targetBlock = hoistBlock;
+ // ... and see if we can lift it one more level
+ hoistBlock = getHoistBlock(targetBlock);
+ }
+ }
+
+ qCDebug(lcSched) << ".. fixating it in block" << targetBlock->index()
+ << "where the minimum block was" << minBlock->index();
+
+ placeFixed(node, targetBlock, DontSchedule);
+ enqueueInputs(node, todo);
+}
+
+void NodeScheduler::enqueueInputs(Node *node, NodeWorkList &todo)
+{
+ for (Node *input : node->inputs()) {
+ if (!input)
+ continue;
+ if (!needsScheduling(input))
+ continue;
+ if (!isLive(input))
+ continue;
+ auto sd = schedulerData(input);
+ if (sd->isFixed)
+ continue;
+ qCDebug(lcSched).noquote() << "... enqueueing input node" << input->id()
+ << todo.status(input);
+ if (sd->unscheduledUses != SchedulerData::NotYetCalculated) {
+ if (sd->unscheduledUses > 0)
+ --sd->unscheduledUses;
+ if (sd->unscheduledUses == 0)
+ todo.reEnqueue(input);
+ } else {
+ todo.reEnqueue(input);
+ }
+ }
+}
+
+Node *NodeScheduler::firstNotFixedUse(Node *node)
+{
+ for (Node *use : node->uses()) {
+ if (!isLive(use))
+ continue;
+ if (!schedulerData(use)->isFixed)
+ return use;
+ }
+ return nullptr;
+}
+
+MIBlock *NodeScheduler::commonDominatorOfUses(Node *node)
+{
+ MIBlock *commonDominator = nullptr;
+ for (auto useIt = node->uses().begin(), useEIt = node->uses().end(); useIt != useEIt; ++useIt) {
+ Node *use = *useIt;
+ if (!isLive(use))
+ continue;
+ // region nodes use other nodes through their control dependency. But those nodes should
+ // already have been placed as block terminators before.
+ Q_ASSERT(use->opcode() != Meta::Region);
+ if (use->opcode() == Meta::Phi || use->opcode() == Meta::EffectPhi) {
+ // find the predecessor block defining this input
+ Node *region = use->controlInput(0);
+ Node *input = region->controlInput(useIt.inputIndex());
+ use = input;
+ }
+ auto minBlock = schedulerData(use)->minimumBlock;
+ if (commonDominator == nullptr)
+ commonDominator = minBlock;
+ else
+ commonDominator = getCommonDominator(commonDominator, minBlock);
+ }
+ return commonDominator;
+}
+
+void NodeScheduler::scheduleNodesInBlock()
+{
+ auto startBlock = m_miFunction->block(MIFunction::StartBlockIndex);
+ for (Node *n : m_live.reachable()) {
+ auto sd = schedulerData(n);
+ if (!sd->minimumBlock)
+ sd->minimumBlock = startBlock;
+ }
+
+ std::vector<std::vector<SchedulerData *>> nodesForBlock;
+ nodesForBlock.resize(m_miFunction->blockCount());
+
+ for (auto sd : m_schedulerData) {
+ if (sd == nullptr)
+ continue;
+ if (!isLive(sd->node))
+ continue;
+ sd->unscheduledUses = 0;
+ for (Node *use : sd->node->uses()) {
+ if (!needsScheduling(use))
+ continue;
+ if (schedulerData(use)->isScheduledInBlock)
+ continue;
+ if (schedulerData(use)->minimumBlock == sd->minimumBlock)
+ ++sd->unscheduledUses;
+ }
+ if (sd->unscheduledUses == 0)
+ nodesForBlock[sd->minimumBlock->index()].push_back(sd);
+ }
+
+ NodeWorkList todo(m_irFunction->graph());
+ for (MIBlock *b : m_miFunction->blocks()) {
+ qCDebug(lcSched) << "Scheduling inside block" << b->index();
+ MIInstr *insertionPoint = &b->instructions().back();
+ todo.enqueue(insertionPoint->irNode());
+ scheduleNodesInBlock(insertionPoint, b, todo);
+ Q_ASSERT(todo.isEmpty());
+ for (auto sd : nodesForBlock[b->index()]) {
+ if (!sd->isScheduledInBlock)
+ todo.enqueue(sd->node);
+ }
+ scheduleNodesInBlock(insertionPoint, b, todo);
+ Q_ASSERT(todo.isEmpty());
+ todo.reset();
+ }
+}
+
+void NodeScheduler::scheduleNodesInBlock(MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo)
+{
+ while (Node *n = todo.dequeueNextNodeForVisiting())
+ scheduleNodeInBlock(n, insertionPoint, b, todo);
+}
+
+void NodeScheduler::scheduleNodeInBlock(Node *node, MIInstr *&insertionPoint, MIBlock *b,
+ NodeWorkList &todo)
+{
+ Q_ASSERT(!node->isDead());
+
+ if (!isLive(node))
+ return;
+
+ if (!needsScheduling(node))
+ return;
+
+ auto nodeData = schedulerData(node);
+ if (nodeData->minimumBlock != b)
+ return;
+
+ const bool wasAlreadyScheduled = nodeData->isScheduledInBlock;
+ if (!wasAlreadyScheduled) {
+ if (nodeData->unscheduledUses)
+ return;
+
+ scheduleNodeNow(node, insertionPoint);
+ }
+
+ if (Node *framestate = node->frameStateInput())
+ scheduleNodeInBlock(framestate, insertionPoint, b, todo);
+
+ for (Node *input : node->inputs()) {
+ if (!input)
+ continue;
+ if (!needsScheduling(input))
+ continue;
+ if (!isLive(input))
+ continue;
+ auto inputInfo = schedulerData(input);
+ if (inputInfo->isScheduledInBlock)
+ continue;
+ Q_ASSERT(inputInfo->minimumBlock != nullptr);
+ if (inputInfo->minimumBlock != b)
+ continue;
+ Q_ASSERT(!input->isDead());
+ Q_ASSERT(inputInfo->unscheduledUses != SchedulerData::NotYetCalculated);
+ if (!wasAlreadyScheduled && inputInfo->unscheduledUses > 0)
+ --inputInfo->unscheduledUses;
+ if (inputInfo->unscheduledUses == 0)
+ todo.enqueue(input);
+ }
+}
+
+void NodeScheduler::scheduleNodeNow(Node *node, MIInstr *&insertionPoint)
+{
+ qCDebug(lcSched) << ".. scheduling node" << node->id()
+ << "in block" << insertionPoint->parent()->index()
+ << "before node" << insertionPoint->irNode()->id();
+
+ MIInstr *newInstr = createMIInstruction(node);
+ newInstr->insertBefore(insertionPoint);
+ insertionPoint = newInstr;
+}
+
+void NodeScheduler::place(Node *node, MIBlock *b)
+{
+ Q_ASSERT(!node->isDead());
+
+ if (b == nullptr)
+ return;
+
+ schedulerData(node)->minimumBlock = b;
+}
+
+void NodeScheduler::placeFixed(Node *node, MIBlock *b, ScheduleOrNot markScheduled)
+{
+ place(node, b);
+ auto sd = schedulerData(node);
+ Q_ASSERT(!sd->isFixed);
+ sd->isFixed = true;
+ sd->isScheduledInBlock = markScheduled == Schedule;
+}
+
+unsigned NodeScheduler::vregForNode(Node *node)
+{
+ unsigned &vreg = m_vregs[unsigned(node->id())];
+ if (vreg == std::numeric_limits<unsigned>::max())
+ vreg = m_nextVReg++;
+ return vreg;
+}
+
+void NodeScheduler::addBlockStart(std::vector<Node *> &roots, Node *startNode, MIBlock *block)
+{
+ block->instructions().insert(block->instructions().begin(), createMIInstruction(startNode));
+ if (startNode->opcode() == Meta::Region) {
+ for (Node *use : startNode->uses()) {
+ if (use->opcode() == Meta::Phi && isLive(use)) {
+ block->addArgument(MIOperand::createVirtualRegister(use, vregForNode(use)));
+ placeFixed(use, block, Schedule);
+ roots.push_back(use);
+ } else if (use->opcode() == Meta::EffectPhi && isLive(use)) {
+ placeFixed(use, block, Schedule);
+ roots.push_back(use);
+ }
+ }
+ }
+}
+
+void NodeScheduler::handleStartNode(Node *startNode, MIBlock *startBlock)
+{
+ startBlock->instructions().push_front(createMIInstruction(startNode));
+
+ QVarLengthArray<Node *, 32> args;
+ for (Node *use : startNode->uses()) {
+ switch (use->opcode()) {
+ case Meta::Engine: Q_FALLTHROUGH();
+ case Meta::CppFrame:
+ case Meta::Function:
+ placeFixed(use, startBlock, Schedule);
+ break;
+ case Meta::Parameter: {
+ auto param = ParameterPayload::get(*use->operation());
+ int idx = int(param->parameterIndex());
+ if (args.size() <= idx)
+ args.resize(idx + 1);
+ args[int(idx)] = use;
+ placeFixed(use, startBlock, Schedule);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (unsigned i = 0, ei = unsigned(args.size()); i != ei; ++i) {
+ if (Node *arg = args.at(int(i)))
+ startBlock->addArgument(MIOperand::createJSStackSlot(arg, i));
+ }
+}
+
+static Node *firstControlOutput(Node *n)
+{
+ for (auto it = n->uses().begin(), eit = n->uses().end(); it != eit; ++it) {
+ if (it.isUsedAsControl())
+ return *it;
+ }
+ return nullptr;
+}
+
+MIInstr *NodeScheduler::createMIInstruction(Node *node)
+{
+ const auto opcode = node->operation()->kind();
+
+ unsigned nArgs = 0;
+ switch (opcode) {
+ case Meta::UnwindDispatch: Q_FALLTHROUGH();
+ case Meta::Jump: {
+ Node *target = firstControlOutput(node);
+ if (target->opcode() == Meta::Region) {
+ for (Node *n : target->uses()) {
+ if (n->opcode() == Meta::Phi && isLive(n))
+ ++nArgs;
+ }
+ }
+ }
+ break;
+ case Meta::Branch:
+ nArgs = 1;
+ break;
+ case Meta::Return:
+ nArgs = 1;
+ break;
+ default:
+ nArgs = node->operation()->valueInputCount();
+ break;
+ }
+
+ MIInstr *instr = MIInstr::create(m_irFunction->pool(), node, nArgs);
+ for (unsigned i = 0, ei = node->operation()->valueInputCount(); i != ei; ++i)
+ instr->setOperand(i, createMIOperand(node->input(i)));
+ if (node->opcode() != Meta::Start && node->operation()->valueOutputCount() > 0)
+ instr->setDestination(createMIOperand(node));
+
+ schedulerData(node)->isScheduledInBlock = true;
+ return instr;
+}
+
+MIOperand NodeScheduler::createMIOperand(Node *node)
+{
+ if (node->operation()->isConstant())
+ return MIOperand::createConstant(node);
+
+ auto opcode = node->operation()->kind();
+ switch (opcode) {
+ case Meta::Parameter:
+ return MIOperand::createJSStackSlot(
+ node, unsigned(ParameterPayload::get(*node->operation())->parameterIndex()));
+ case Meta::Engine:
+ return MIOperand::createEngineRegister(node);
+ case Meta::CppFrame:
+ return MIOperand::createCppFrameRegister(node);
+ case Meta::Function:
+ return MIOperand::createFunction(node);
+ default:
+ if ((node->opcode() == Meta::Call
+ && CallPayload::get(*node->operation())->callee() == Meta::JSThisToObject)
+ || node->opcode() == Meta::StoreThis) {
+ return MIOperand::createJSStackSlot(node, CallData::This);
+ } else {
+ return MIOperand::createVirtualRegister(node, vregForNode(node));
+ }
+ }
+}
+
+void NodeScheduler::showNodesByBlock(const QString &description) const
+{
+ if (!lcSched().isDebugEnabled())
+ return;
+
+ qCDebug(lcSched) << description;
+
+ for (MIBlock *b : m_miFunction->blocks()) {
+ QString s;
+ for (const SchedulerData *sd : m_schedulerData) {
+ if (!sd)
+ continue;
+ if (!isLive(sd->node))
+ continue;
+ if (sd->minimumBlock == b) {
+ if (!s.isEmpty())
+ s += QStringLiteral(", ");
+ s += QStringLiteral("%1 (%2)").arg(QString::number(sd->node->id()),
+ sd->node->operation()->debugString());
+ }
+ }
+ if (s.isEmpty())
+ s = QStringLiteral("<<none>>");
+ qCDebug(lcSched, "Nodes in block %u: %s", b->index(), s.toUtf8().constData());
+ }
+}
+
+void NodeScheduler::dumpDotCFG() const
+{
+ QString out;
+ out += QLatin1Char('\n');
+ out += QStringLiteral("digraph{root=\"L%1\" label=\"Control Flow Graph\";"
+ "node[shape=circle];edge[dir=forward fontsize=10]\n")
+ .arg(MIFunction::StartBlockIndex);
+ for (MIBlock *src : m_miFunction->blocks()) {
+ for (MIBlock *dst : src->outEdges()) {
+ out += QStringLiteral("L%1->L%2\n").arg(QString::number(src->index()),
+ QString::number(dst->index()));
+ }
+ }
+ out += QStringLiteral("}\n");
+ qCDebug(lcDotCFG).nospace().noquote() << out;
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4schedulers_p.h b/src/qml/jit/qv4schedulers_p.h
new file mode 100644
index 0000000000..f9179816df
--- /dev/null
+++ b/src/qml/jit/qv4schedulers_p.h
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4SCHEDULER_P_H
+#define QV4SCHEDULER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4global_p.h"
+#include "qv4mi_p.h"
+#include "qv4node_p.h"
+#include "qv4domtree_p.h"
+#include "qv4loopinfo_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+// Node scheduling "flattens" the graph into basic blocks with an ordered list of instructions.
+//
+// The various steps are mentioned in buildMIFunction, but the general idea is described in
+// https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf in chapter 6.
+class NodeScheduler final
+{
+ Q_DISABLE_COPY_MOVE(NodeScheduler)
+
+ class SchedulerData final {
+ Q_DISABLE_COPY_MOVE(SchedulerData)
+ public:
+ static SchedulerData *create(QQmlJS::MemoryPool *pool)
+ { return pool->New<SchedulerData>(); }
+
+ SchedulerData() = default;
+ ~SchedulerData() = default;
+
+ Node *node = nullptr;
+ MIBlock *minimumBlock = nullptr;
+ bool isFixed = false;
+ bool isScheduledInBlock = false;
+ static constexpr unsigned NotYetCalculated = std::numeric_limits<unsigned>::max();
+ unsigned unscheduledUses = NotYetCalculated;
+ };
+
+public:
+ NodeScheduler(Function *irFunction);
+ ~NodeScheduler() = default;
+
+ MIFunction *buildMIFunction();
+
+private:
+ std::vector<Node *> buildCFG();
+ void scheduleEarly(const std::vector<Node *> &roots);
+ void scheduleEarly(Node *node, NodeWorkList &todo);
+ void propagateMinimumPosition(MIBlock *newMinimumPosition, Node *toNode, NodeWorkList &todo);
+ void scheduleLate(const std::vector<Node *> &roots);
+ void scheduleNodeLate(Node *node, NodeWorkList &todo);
+ void enqueueInputs(Node *node, NodeWorkList &todo);
+ Node *firstNotFixedUse(Node *node);
+ MIBlock *commonDominatorOfUses(Node *node);
+ void scheduleNodesInBlock();
+ void scheduleNodesInBlock(MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo);
+ void scheduleNodeInBlock(Node *node, MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo);
+ void scheduleNodeNow(Node *node, MIInstr *&insertionPoint);
+
+ void place(Node *node, MIBlock *b);
+ enum ScheduleOrNot { DontSchedule, Schedule };
+ void placeFixed(Node *node, MIBlock *b, ScheduleOrNot markScheduled);
+ unsigned vregForNode(Node *node);
+ void addBlockStart(std::vector<Node *> &roots, Node *startNode, MIBlock *block);
+ void enqueueControlInputs(Node *node);
+ void handleStartNode(Node *startNode, MIBlock *startBlock);
+ MIInstr *createMIInstruction(Node *node);
+ MIOperand createMIOperand(Node *node);
+ SchedulerData *schedulerData(Node *n)
+ {
+ if (Q_UNLIKELY(m_schedulerData.size() <= n->id()))
+ m_schedulerData.resize(n->id() + 8);
+ SchedulerData *&sd = m_schedulerData[n->id()];
+ if (Q_UNLIKELY(sd == nullptr)) {
+ sd = SchedulerData::create(m_irFunction->pool());
+ sd->node = n;
+ }
+ return sd;
+ }
+ bool isLive(Node *n) const
+ { return m_live.isReachable(n->id()); }
+ bool canStartBlock(Node *node) const;
+ bool isControlFlowSplit(Node *node) const;
+ bool isBlockTerminator(Node *node) const;
+ MIBlock *getCommonDominator(MIBlock *one, MIBlock *other) const;
+ MIBlock *getHoistBlock(MIBlock *block) const;
+
+ void showNodesByBlock(const QString &description) const;
+
+ void dumpDotCFG() const;
+
+private:
+ Function *m_irFunction = nullptr;
+ MIFunction *m_miFunction = nullptr;
+ QScopedPointer<LoopInfo> m_loopInfo;
+ QScopedPointer<DominatorTree> m_domTree;
+ std::vector<int> m_dominatorDepthForBlock;
+ std::vector<unsigned> m_vregs;
+ std::vector<SchedulerData *> m_schedulerData;
+ NodeCollector m_live;
+ unsigned m_nextVReg = 0;
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4SCHEDULER_P_H
diff --git a/src/qml/jit/qv4tracingjit.cpp b/src/qml/jit/qv4tracingjit.cpp
new file mode 100644
index 0000000000..c8974b3a1b
--- /dev/null
+++ b/src/qml/jit/qv4tracingjit.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qloggingcategory.h>
+
+#include "qv4vme_moth_p.h"
+#include "qv4graphbuilder_p.h"
+#include "qv4lowering_p.h"
+#include "qv4mi_p.h"
+#include "qv4schedulers_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcTracing, "qt.v4.tracing")
+
+namespace QV4 {
+
+// This is the entry point for the "tracing JIT". It uses the sea-of-nodes concept as described in
+// https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf
+//
+// The minimal pipeline is as follows:
+// - create the graph for the function
+// - do generic lowering
+// - schedule the nodes
+// - run minimal stack slot allocation (no re-use of slots)
+// - run the assembler
+//
+// This pipeline has no optimizations, and generates quite inefficient code. It does have the
+// advantage that no trace information is used, so it can be used for testing where it replaces
+// the baseline JIT. Any optimizations are additions to this pipeline.
+//
+// Note: generators (or resuming functions in general) are not supported by this JIT.
+void Moth::runTracingJit(QV4::Function *function)
+{
+ IR::Function irFunction(function);
+ qCDebug(lcTracing).noquote() << "runTracingJit called for" << irFunction.name() << "...";
+
+ qCDebug(lcTracing).noquote().nospace() << function->traceInfoToString();
+
+ IR::GraphBuilder::buildGraph(&irFunction);
+ irFunction.dump(QStringLiteral("initial IR"));
+ irFunction.verify();
+
+ IR::GenericLowering(irFunction).lower();
+ irFunction.dump(QStringLiteral("after generic lowering"));
+ irFunction.verify();
+
+ IR::NodeScheduler scheduler(&irFunction);
+ QScopedPointer<IR::MIFunction> miFunction(scheduler.buildMIFunction());
+ miFunction->dump(QStringLiteral("initial MI"));
+ irFunction.verify();
+}
+
+} // QV4 namespace
+QT_END_NAMESPACE