aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jit/qv4graphbuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jit/qv4graphbuilder.cpp')
-rw-r--r--src/qml/jit/qv4graphbuilder.cpp1683
1 files changed, 1683 insertions, 0 deletions
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