aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@me.com>2013-09-01 15:45:53 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-13 11:41:23 +0200
commit9c82c105a1886473ca144b802ce9f5bec01e35e8 (patch)
tree2caeb5945e2b60f49ba046ec50a45c0bec4f2f7d /src/qml
parent0ef673efe8bf381e1aa0202752deac27e86ada53 (diff)
V4: Fix SSA decomposition when no regalloc is used.
Add scheduling for moves generated by removing phi nodes by re-using the MoveMapping class from the register allocator. This change applies to both the JIT when no register allocator is used, and the interpreter. Change-Id: I38eac5b13759c7790132d1ef07fa17fcb53187e3 Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/compiler/qv4codegen.cpp3
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h7
-rw-r--r--src/qml/compiler/qv4isel_masm.cpp3
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp29
-rw-r--r--src/qml/compiler/qv4regalloc.cpp137
-rw-r--r--src/qml/compiler/qv4ssa.cpp276
-rw-r--r--src/qml/compiler/qv4ssa_p.h46
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp4
8 files changed, 257 insertions, 248 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 293086378e..4b2c0245a4 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -61,9 +61,6 @@
#undef CONST
#endif
-#define QV4_NO_LIVENESS
-#undef SHOW_SSA
-
using namespace QQmlJS;
using namespace AST;
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index 25c8ac512c..af1ca0ae76 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE
F(LoadRegExp, loadRegExp) \
F(LoadClosure, loadClosure) \
F(MoveTemp, moveTemp) \
+ F(SwapTemps, swapTemps) \
F(LoadName, loadName) \
F(StoreName, storeName) \
F(LoadElement, loadElement) \
@@ -228,6 +229,11 @@ union Instr
Param source;
Param result;
};
+ struct instr_swapTemps {
+ MOTH_INSTR_HEADER
+ Param left;
+ Param right;
+ };
struct instr_loadClosure {
MOTH_INSTR_HEADER
int value;
@@ -505,6 +511,7 @@ union Instr
instr_loadRuntimeString loadRuntimeString;
instr_loadRegExp loadRegExp;
instr_moveTemp moveTemp;
+ instr_swapTemps swapTemps;
instr_loadClosure loadClosure;
instr_loadName loadName;
instr_storeName storeName;
diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp
index 7905ffe977..d0477b2387 100644
--- a/src/qml/compiler/qv4isel_masm.cpp
+++ b/src/qml/compiler/qv4isel_masm.cpp
@@ -601,7 +601,6 @@ void InstructionSelection::run(int functionIndex)
qSwap(_function, function);
qSwap(_reentryBlocks, reentryBlocks);
-
V4IR::Optimizer opt(_function);
opt.run();
@@ -1165,8 +1164,6 @@ void InstructionSelection::swapValues(V4IR::Temp *sourceTemp, V4IR::Temp *target
}
}
- Q_ASSERT(sourceTemp->type == targetTemp->type);
-
V4IR::Temp *stackTemp = sourceTemp->kind == V4IR::Temp::StackSlot ? sourceTemp : targetTemp;
V4IR::Temp *registerTemp = sourceTemp->kind == V4IR::Temp::PhysicalRegister ? sourceTemp
: targetTemp;
diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp
index fd789a623f..60945aa3b0 100644
--- a/src/qml/compiler/qv4isel_moth.cpp
+++ b/src/qml/compiler/qv4isel_moth.cpp
@@ -168,6 +168,8 @@ public:
private:
int allocateSlot(V4IR::Temp *t, V4IR::Stmt *currentStmt) {
+ Q_ASSERT(currentStmt->id > 0);
+
const V4IR::LifeTimeInterval &interval = _intervals[*t];
int idx = _hints.value(*t, -1);
if (idx != -1 && _activeSlots[idx] <= currentStmt->id) {
@@ -231,8 +233,10 @@ void InstructionSelection::run(int functionIndex)
V4IR::Optimizer opt(_function);
opt.run();
StackSlotAllocator *stackSlotAllocator = 0;
- if (opt.isInSSA())
+ if (opt.isInSSA()) {
stackSlotAllocator = new StackSlotAllocator(opt.lifeRanges(), _function->tempCount);
+ opt.convertOutOfSSA();
+ }
qSwap(_stackSlotAllocator, stackSlotAllocator);
V4IR::Stmt *cs = 0;
qSwap(_currentStatement, cs);
@@ -258,21 +262,6 @@ void InstructionSelection::run(int functionIndex)
if (s->location.isValid())
lineNumberMappings << _codeNext - _codeStart << s->location.startLine;
- if (opt.isInSSA() && s->asTerminator()) {
- foreach (const V4IR::Optimizer::SSADeconstructionMove &move,
- opt.ssaDeconstructionMoves(_block)) {
- if (V4IR::Const *c = move.source->asConst()) {
- loadConst(c, move.target);
- } else {
- Q_ASSERT(move.source->asTemp());
- if (move.needsConversion())
- convertType(move.source->asTemp(), move.target);
- else
- copyValue(move.source->asTemp(), move.target);
- }
- }
- }
-
s->accept(this);
}
}
@@ -498,10 +487,12 @@ void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetT
addInstruction(move);
}
-void InstructionSelection::swapValues(V4IR::Temp *, V4IR::Temp *)
+void InstructionSelection::swapValues(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp)
{
- // This is generated by the register allocator for the JIT, so it cannot end up here.
- Q_UNREACHABLE();
+ Instruction::SwapTemps swap;
+ swap.left = getParam(sourceTemp);
+ swap.right = getParam(targetTemp);
+ addInstruction(swap);
}
void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp)
diff --git a/src/qml/compiler/qv4regalloc.cpp b/src/qml/compiler/qv4regalloc.cpp
index 3fb0d22e66..a6364a25d4 100644
--- a/src/qml/compiler/qv4regalloc.cpp
+++ b/src/qml/compiler/qv4regalloc.cpp
@@ -758,143 +758,6 @@ private:
}
}
- class MoveMapping
- {
- struct Move {
- Expr *from;
- Temp *to;
- bool needsSwap;
-
- Move(Expr *from, Temp *to)
- : from(from), to(to), needsSwap(false)
- {}
-
- bool operator==(const Move &other) const
- { return from == other.from && to == other.to; }
- };
-
- QList<Move> _moves;
-
- int isUsedAsSource(Expr *e) const
- {
- if (Temp *t = e->asTemp())
- for (int i = 0, ei = _moves.size(); i != ei; ++i)
- if (Temp *from = _moves[i].from->asTemp())
- if (*from == *t)
- return i;
-
- return -1;
- }
-
- public:
- void add(Expr *from, Temp *to) {
- if (Temp *t = from->asTemp())
- if (*t == *to)
- return;
-
- Move m(from, to);
- if (_moves.contains(m))
- return;
- _moves.append(m);
- }
-
- void order()
- {
- QList<Move> todo = _moves;
- QList<Move> output, swaps;
- output.reserve(_moves.size());
- QList<Move> delayed;
- delayed.reserve(_moves.size());
-
- while (!todo.isEmpty()) {
- const Move m = todo.first();
- todo.removeFirst();
- schedule(m, todo, delayed, output, swaps);
- }
-
- output += swaps;
-
- Q_ASSERT(todo.isEmpty());
- Q_ASSERT(delayed.isEmpty());
- qSwap(_moves, output);
- }
-
-#if defined(DEBUG_REGALLOC)
- void dump() const
- {
- QTextStream os(stdout, QIODevice::WriteOnly);
- os << "Move mapping has " << _moves.size() << " moves..." << endl;
- foreach (const Move &m, _moves) {
- os << "\t";
- m.to->dump(os);
- if (m.needsSwap)
- os << " <-> ";
- else
- os << " <-- ";
- m.from->dump(os);
- os << endl;
- }
- }
-#endif // DEBUG_REGALLOC
-
- void insertMoves(BasicBlock *predecessor, Function *function) const
- {
- int predecessorInsertionPoint = predecessor->statements.size() - 1;
- foreach (const Move &m, _moves) {
- V4IR::Move *move = function->New<V4IR::Move>();
- move->init(m.to, m.from, OpInvalid);
- move->swap = m.needsSwap;
- predecessor->statements.insert(predecessorInsertionPoint++, move);
- }
- }
-
- private:
- enum Action { NormalMove, NeedsSwap };
- Action schedule(const Move &m, QList<Move> &todo, QList<Move> &delayed, QList<Move> &output,
- QList<Move> &swaps) const
- {
- int useIdx = isUsedAsSource(m.to);
- if (useIdx != -1) {
- const Move &dependency = _moves[useIdx];
- if (!output.contains(dependency)) {
- if (delayed.contains(dependency)) {
- // We have a cycle! Break it by swapping instead of assigning.
-#if defined(DEBUG_REGALLOC)
- delayed+=m;
- QTextStream out(stderr, QIODevice::WriteOnly);
- out<<"we have a cycle! temps:" << endl;
- foreach (const Move &m, delayed) {
- out<<"\t";
- m.to->dump(out);
- out<<" <- ";
- m.from->dump(out);
- out<<endl;
- }
- delayed.removeOne(m);
-#endif
- return NeedsSwap;
- } else {
- delayed.append(m);
- todo.removeOne(dependency);
- Action action = schedule(dependency, todo, delayed, output, swaps);
- delayed.removeOne(m);
- Move mm(m);
- if (action == NeedsSwap) {
- mm.needsSwap = true;
- swaps.append(mm);
- } else {
- output.append(mm);
- }
- return action;
- }
- }
- }
-
- output.append(m);
- return NormalMove;
- }
- };
-
void resolveEdge(BasicBlock *predecessor, BasicBlock *successor)
{
#ifdef DEBUG_REGALLOC
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
index 541793f6ae..5f62adb1b1 100644
--- a/src/qml/compiler/qv4ssa.cpp
+++ b/src/qml/compiler/qv4ssa.cpp
@@ -62,6 +62,7 @@
#define QV4_NO_LIVENESS
#undef SHOW_SSA
+#undef DEBUG_MOVEMAPPING
QT_USE_NAMESPACE
@@ -1961,6 +1962,19 @@ inline Temp *isSameTempPhi(Phi *phi)
return 0;
}
+static Expr *clone(Expr *e, Function *function) {
+ if (Temp *t = e->asTemp()) {
+ return CloneExpr::cloneTemp(t, function);
+ } else if (Const *c = e->asConst()) {
+ return CloneExpr::cloneConst(c, function);
+ } else if (Name *n = e->asName()) {
+ return CloneExpr::cloneName(n, function);
+ } else {
+ Q_UNREACHABLE();
+ return e;
+ }
+}
+
class ExprReplacer: public StmtVisitor, public ExprVisitor
{
DefUsesCalculator &_defUses;
@@ -2034,22 +2048,9 @@ protected:
}
private:
- Expr *clone(Expr *e) const {
- if (Temp *t = e->asTemp()) {
- return CloneExpr::cloneTemp(t, _function);
- } else if (Const *c = e->asConst()) {
- return CloneExpr::cloneConst(c, _function);
- } else if (Name *n = e->asName()) {
- return CloneExpr::cloneName(n, _function);
- } else {
- Q_UNIMPLEMENTED();
- return e;
- }
- }
-
void check(Expr *&e) {
if (equals(e, _toReplace))
- e = clone(_replacement);
+ e = clone(_replacement, _function);
else
e->accept(this);
}
@@ -2274,7 +2275,6 @@ void optimizeSSA(Function *function, DefUsesCalculator &defUses)
*ref[s] = 0;
continue;
}
-
} else if (Move *m = s->asMove()) {
if (Temp *t = unescapableTemp(m->target, variablesCanEscape)) {
// constant propagation:
@@ -2282,13 +2282,13 @@ void optimizeSSA(Function *function, DefUsesCalculator &defUses)
if (c->type & NumberType || c->type == BoolType) {
// TODO: when propagating other constants, e.g. undefined, the other
// optimization passes have to be changed to cope with them.
-// qout<<"propagating constant from ";s->dump(qout);qout<<" info:"<<endl;
W += replaceUses(t, c);
defUses.removeDef(*t);
*ref[s] = 0;
}
continue;
}
+
#if defined(PROPAGATE_THIS)
if (Name *n = m->source->asName()) {
qout<<"propagating constant from ";s->dump(qout);qout<<" info:"<<endl;
@@ -2298,6 +2298,7 @@ void optimizeSSA(Function *function, DefUsesCalculator &defUses)
continue;
}
#endif
+
// copy propagation:
if (Temp *t2 = unescapableTemp(m->source, variablesCanEscape)) {
QVector<Stmt *> newT2Uses = replaceUses(t, t2);
@@ -2428,6 +2429,7 @@ void optimizeSSA(Function *function, DefUsesCalculator &defUses)
continue;
}
}
+
} else if (CJump *cjump = s->asCJump()) {
if (Const *c = cjump->cond->asConst()) {
// Note: this assumes that there are no critical edges! Meaning, we can safely purge
@@ -2836,76 +2838,55 @@ void Optimizer::run()
}
}
-namespace {
-void insertMove(Function *function, BasicBlock *basicBlock, Temp *target, Expr *source) {
- if (target->type != source->type) {
- if (source->asConst()) {
- const int idx = function->tempCount++;
+void Optimizer::convertOutOfSSA() {
+ if (!inSSA)
+ return;
- Temp *tmp = function->New<Temp>();
- tmp->init(Temp::VirtualRegister, idx, 0);
+ // There should be no critical edges at this point.
- Move *s = function->New<Move>();
- s->init(tmp, source, OpInvalid);
- basicBlock->statements.insert(basicBlock->statements.size() - 1, s);
+ foreach (BasicBlock *bb, function->basicBlocks) {
+ const int id = bb->statements.last()->id;
+ MoveMapping moves;
- tmp = function->New<Temp>();
- tmp->init(Temp::VirtualRegister, idx, 0);
- source = tmp;
+ foreach (BasicBlock *successor, bb->out) {
+ const int inIdx = successor->in.indexOf(bb);
+ Q_ASSERT(inIdx >= 0);
+ foreach (Stmt *s, successor->statements) {
+ if (Phi *phi = s->asPhi()) {
+ moves.add(clone(phi->d->incoming[inIdx], function),
+ clone(phi->targetTemp, function)->asTemp(), id);
+ } else {
+ break;
+ }
+ }
}
- source = basicBlock->CONVERT(source, target->type);
- }
- Move *s = function->New<Move>();
- s->init(target, source, OpInvalid);
- basicBlock->statements.insert(basicBlock->statements.size() - 1, s);
-}
-}
+ #if defined(DEBUG_MOVEMAPPING)
+ QTextStream os(stdout, QIODevice::WriteOnly);
+ os << "Move mapping for function ";
+ if (function->name)
+ os << *function->name;
+ else
+ os << (void *) function;
+ os << " on basic-block L" << bb->index << ":" << endl;
+ moves.dump();
+ #endif // DEBUG_MOVEMAPPING
-/*
- * Quick function to convert out of SSA, so we can put the stuff through the ISel phases. This
- * has to be replaced by a phase in the specific ISel back-ends and do register allocation at the
- * same time. That way the huge number of redundant moves generated by this function are eliminated.
- */
-void Optimizer::convertOutOfSSA() {
- // We assume that edge-splitting is already done.
- foreach (BasicBlock *bb, function->basicBlocks) {
- QVector<Stmt *> &stmts = bb->statements;
- while (!stmts.isEmpty()) {
- Stmt *s = stmts.first();
- if (Phi *phi = s->asPhi()) {
- stmts.removeFirst();
- for (int i = 0, ei = phi->d->incoming.size(); i != ei; ++i)
- insertMove(function, bb->in[i], phi->targetTemp, phi->d->incoming[i]);
- phi->destroyData();
- } else {
- break;
- }
- }
- }
-}
+ moves.order();
-QList<Optimizer::SSADeconstructionMove> Optimizer::ssaDeconstructionMoves(BasicBlock *basicBlock) const
-{
- QList<SSADeconstructionMove> moves;
+ moves.insertMoves(bb, function);
+ }
- foreach (BasicBlock *outEdge, basicBlock->out) {
- int inIdx = outEdge->in.indexOf(basicBlock);
- Q_ASSERT(inIdx >= 0);
- foreach (Stmt *s, outEdge->statements) {
- if (Phi *phi = s->asPhi()) {
- SSADeconstructionMove m;
- m.phi = phi;
- m.source = phi->d->incoming[inIdx];
- m.target = phi->targetTemp;
- moves.append(m);
+ foreach (BasicBlock *bb, function->basicBlocks) {
+ while (!bb->statements.isEmpty()) {
+ if (Phi *phi = bb->statements.first()->asPhi()) {
+ phi->destroyData();
+ bb->statements.removeFirst();
} else {
break;
}
}
}
-
- return moves;
}
QList<LifeTimeInterval> Optimizer::lifeRanges() const
@@ -2922,3 +2903,152 @@ void Optimizer::showMeTheCode(Function *function)
{
::showMeTheCode(function);
}
+
+static inline bool overlappingStorage(const Temp &t1, const Temp &t2)
+{
+ // This is the same as the operator==, but for one detail: memory locations are not sensitive
+ // to types, and neither are general-purpose registers.
+
+ if (t1.scope != t2.scope)
+ return false;
+ if (t1.index != t2.index)
+ return false; // different position, where-ever that may (physically) be.
+ if (t1.kind != t2.kind)
+ return false; // formal/local/(physical-)register/stack do never overlap
+ if (t1.kind != Temp::PhysicalRegister) // Other than registers, ...
+ return t1.kind == t2.kind; // ... everything else overlaps: any memory location can hold everything.
+
+ // So now the index is the same, and we know that both stored in a register. If both are
+ // floating-point registers, they are the same. Or, if both are non-floating-point registers,
+ // generally called general-purpose registers, they are also the same.
+ return (t1.type == DoubleType && t2.type == DoubleType)
+ || (t1.type != DoubleType && t2.type != DoubleType);
+}
+
+int MoveMapping::isUsedAsSource(Expr *e) const
+{
+ if (Temp *t = e->asTemp())
+ for (int i = 0, ei = _moves.size(); i != ei; ++i)
+ if (Temp *from = _moves[i].from->asTemp())
+ if (overlappingStorage(*from, *t))
+ return i;
+
+ return -1;
+}
+
+void MoveMapping::add(Expr *from, Temp *to, int id) {
+ if (Temp *t = from->asTemp()) {
+ if (overlappingStorage(*t, *to)) {
+ // assignments like fp1 = fp1 or var{&1} = double{&1} can safely be skipped.
+#if defined(DEBUG_MOVEMAPPING)
+ QTextStream os(stderr, QIODevice::WriteOnly);
+ os << "Skipping ";
+ to->dump(os);
+ os << " <- ";
+ from->dump(os);
+ os << endl;
+#endif // DEBUG_MOVEMAPPING
+ return;
+ }
+ }
+
+ Move m(from, to, id);
+ if (_moves.contains(m))
+ return;
+ _moves.append(m);
+}
+
+void MoveMapping::order()
+{
+ QList<Move> todo = _moves;
+ QList<Move> output, swaps;
+ output.reserve(_moves.size());
+ QList<Move> delayed;
+ delayed.reserve(_moves.size());
+
+ while (!todo.isEmpty()) {
+ const Move m = todo.first();
+ todo.removeFirst();
+ schedule(m, todo, delayed, output, swaps);
+ }
+
+ output += swaps;
+
+ Q_ASSERT(todo.isEmpty());
+ Q_ASSERT(delayed.isEmpty());
+ qSwap(_moves, output);
+}
+
+void MoveMapping::insertMoves(BasicBlock *predecessor, Function *function) const
+{
+ int predecessorInsertionPoint = predecessor->statements.size() - 1;
+ foreach (const Move &m, _moves) {
+ V4IR::Move *move = function->New<V4IR::Move>();
+ move->init(m.to, m.from, OpInvalid);
+ move->id = m.id;
+ move->swap = m.needsSwap;
+ predecessor->statements.insert(predecessorInsertionPoint++, move);
+ }
+}
+
+void MoveMapping::dump() const
+{
+#if defined(DEBUG_MOVEMAPPING)
+ QTextStream os(stdout, QIODevice::WriteOnly);
+ os << "Move mapping has " << _moves.size() << " moves..." << endl;
+ foreach (const Move &m, _moves) {
+ os << "\t";
+ m.to->dump(os);
+ if (m.needsSwap)
+ os << " <-> ";
+ else
+ os << " <-- ";
+ m.from->dump(os);
+ os << endl;
+ }
+#endif // DEBUG_MOVEMAPPING
+}
+
+MoveMapping::Action MoveMapping::schedule(const Move &m, QList<Move> &todo, QList<Move> &delayed,
+ QList<Move> &output, QList<Move> &swaps) const
+{
+ int useIdx = isUsedAsSource(m.to);
+ if (useIdx != -1) {
+ const Move &dependency = _moves[useIdx];
+ if (!output.contains(dependency)) {
+ if (delayed.contains(dependency)) {
+ // We have a cycle! Break it by swapping instead of assigning.
+#if defined(DEBUG_MOVEMAPPING)
+ delayed+=m;
+ QTextStream out(stderr, QIODevice::WriteOnly);
+ out<<"we have a cycle! temps:" << endl;
+ foreach (const Move &m, delayed) {
+ out<<"\t";
+ m.to->dump(out);
+ out<<" <- ";
+ m.from->dump(out);
+ out<<endl;
+ }
+ delayed.removeOne(m);
+#endif // DEBUG_MOVEMAPPING
+ return NeedsSwap;
+ } else {
+ delayed.append(m);
+ todo.removeOne(dependency);
+ Action action = schedule(dependency, todo, delayed, output, swaps);
+ delayed.removeOne(m);
+ Move mm(m);
+ if (action == NeedsSwap) {
+ mm.needsSwap = true;
+ swaps.append(mm);
+ } else {
+ output.append(mm);
+ }
+ return action;
+ }
+ }
+ }
+
+ output.append(m);
+ return NormalMove;
+}
diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h
index 2195cfd9bb..1fabc8e76f 100644
--- a/src/qml/compiler/qv4ssa_p.h
+++ b/src/qml/compiler/qv4ssa_p.h
@@ -118,17 +118,6 @@ public:
class Optimizer
{
public:
- struct SSADeconstructionMove
- {
- Stmt *phi;
- Expr *source;
- Temp *target;
-
- bool needsConversion() const
- { return target->type != source->type; }
- };
-
-public:
Optimizer(Function *function)
: function(function)
, inSSA(false)
@@ -142,8 +131,6 @@ public:
QHash<BasicBlock *, BasicBlock *> loopStartEndBlocks() const { return startEndLoops; }
- QList<SSADeconstructionMove> ssaDeconstructionMoves(BasicBlock *basicBlock) const;
-
QList<LifeTimeInterval> lifeRanges() const;
static void showMeTheCode(Function *function);
@@ -154,6 +141,39 @@ private:
QHash<BasicBlock *, BasicBlock *> startEndLoops;
};
+class MoveMapping
+{
+ struct Move {
+ Expr *from;
+ Temp *to;
+ int id;
+ bool needsSwap;
+
+ Move(Expr *from, Temp *to, int id)
+ : from(from), to(to), id(id), needsSwap(false)
+ {}
+
+ bool operator==(const Move &other) const
+ { return from == other.from && to == other.to; }
+ };
+
+ QList<Move> _moves;
+
+ int isUsedAsSource(Expr *e) const;
+
+public:
+ void add(Expr *from, Temp *to, int id = 0);
+ void order();
+ void insertMoves(BasicBlock *predecessor, Function *function) const;
+
+ void dump() const;
+
+private:
+ enum Action { NormalMove, NeedsSwap };
+ Action schedule(const Move &m, QList<Move> &todo, QList<Move> &delayed, QList<Move> &output,
+ QList<Move> &swaps) const;
+};
+
} // V4IR namespace
} // QQmlJS namespace
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index a2aca79bcb..caeeeb4672 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -258,6 +258,10 @@ QV4::Value VME::run(QV4::ExecutionContext *context, const uchar *&code,
VALUE(instr.result) = VALUE(instr.source);
MOTH_END_INSTR(MoveTemp)
+ MOTH_BEGIN_INSTR(SwapTemps)
+ qSwap(VALUE(instr.left), VALUE(instr.right));
+ MOTH_END_INSTR(MoveTemp)
+
MOTH_BEGIN_INSTR(LoadValue)
// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData());
VALUE(instr.result) = VALUE(instr.value);