aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler/qv4ssa.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/compiler/qv4ssa.cpp')
-rw-r--r--src/qml/compiler/qv4ssa.cpp195
1 files changed, 124 insertions, 71 deletions
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
index 10f0bbcf8f..cc542e94e7 100644
--- a/src/qml/compiler/qv4ssa.cpp
+++ b/src/qml/compiler/qv4ssa.cpp
@@ -3581,16 +3581,43 @@ public:
, _replacement(0)
{}
- void operator()(Temp *toReplace, Expr *replacement, StatementWorklist &W, QVector<Stmt *> *newUses = 0)
+ bool operator()(Temp *toReplace, Expr *replacement, StatementWorklist &W, QVector<Stmt *> *newUses = 0)
{
Q_ASSERT(replacement->asTemp() || replacement->asConst() || replacement->asName());
-// qout << "Replacing ";toReplace->dump(qout);qout<<" by ";replacement->dump(qout);qout<<endl;
-
qSwap(_toReplace, toReplace);
qSwap(_replacement, replacement);
const QVector<Stmt *> &uses = _defUses.uses(*_toReplace);
+
+ // Prevent the following:
+ // L3:
+ // %1 = phi L1: %2, L2: %3
+ // %4 = phi L1: %5, L2: %6
+ // %6 = %1
+ // From turning into:
+ // L3:
+ // %1 = phi L1: %2, L2: %3
+ // %4 = phi L1: %5, L2: %1
+ //
+ // Because both phi nodes are "executed in parallel", we cannot replace %6 by %1 in the
+ // second phi node. So, if the defining statement for a temp is a phi node, and one of the
+ // uses of the to-be-replaced statement is a phi node in the same block as the defining
+ // statement, bail out.
+ if (Temp *r = _replacement->asTemp()) {
+ if (_defUses.defStmt(*r)->asPhi()) {
+ BasicBlock *replacementDefBlock = _defUses.defStmtBlock(*r);
+ for (Stmt *use : uses) {
+ if (Phi *usePhi = use->asPhi()) {
+ if (_defUses.defStmtBlock(*usePhi->targetTemp) == replacementDefBlock)
+ return false;
+ }
+ }
+ }
+ }
+
+// qout << "Replacing ";toReplace->dump(qout);qout<<" by ";replacement->dump(qout);qout<<endl;
+
if (newUses)
newUses->reserve(uses.size());
@@ -3606,6 +3633,7 @@ public:
qSwap(_replacement, replacement);
qSwap(_toReplace, toReplace);
+ return true;
}
private:
@@ -4082,11 +4110,12 @@ void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df)
// copy propagation:
if (Temp *sourceTemp = m->source->asTemp()) {
QVector<Stmt *> newT2Uses;
- replaceUses(targetTemp, sourceTemp, W, &newT2Uses);
- defUses.removeUse(s, *sourceTemp);
- defUses.addUses(*sourceTemp, newT2Uses);
- defUses.removeDef(*targetTemp);
- W.remove(s);
+ if (replaceUses(targetTemp, sourceTemp, W, &newT2Uses)) {
+ defUses.removeUse(s, *sourceTemp);
+ defUses.addUses(*sourceTemp, newT2Uses);
+ defUses.removeDef(*targetTemp);
+ W.remove(s);
+ }
continue;
}
@@ -5635,25 +5664,97 @@ void MoveMapping::add(Expr *from, Temp *to) {
_moves.append(m);
}
+// Order the moves that are generated when resolving edges during register allocation (see [Wimmer1]
+// section 6 for details). Now these moves form one or more graphs, so we have to output them in
+// such an order that values don't get overwritten:
+// r1 <- r0
+// r2 <- r1
+// That input has to be ordered as follows in order to prevent the value in r1 from being lost:
+// r2 <- r1
+// r1 <- r0
+//
+// So, the algorithm is to output the leaves first, and take them out of the input. This will result
+// in some moves to become leaves (in the above example: when leaf r2 <- r1 is generated and taken
+// away, the r1 <- r0 is now a leaf), so we can output those and take those out, and repeat until
+// there are no more leafs.
+//
+// The tricky part is that there might be cycles:
+// r4 <- r5
+// r5 <- r4
+// These have to be turned into a "register swap":
+// r4 <=> r5
+//
+// So after running the above algorithm where we progressively remove the leaves, we are left with
+// zero or more cycles. To resolve those, we break one of the edges of the cycle, and for all other
+// edges we generate swaps. Note that the swaps will always occur as the last couple of moves,
+// because otherwise they might clobber sources for moves:
+// r4 <=> r5
+// r6 <- r5
+// Here, the value of r5 is already overwritten with the one in r4, so the correct order is:
+// r6 <- r5
+// r4 <=> r5
void MoveMapping::order()
{
- QList<Move> todo = _moves;
- QList<Move> output, swaps;
+ QList<Move> output;
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);
- }
+ while (!_moves.isEmpty()) {
+ // Take out all leaf edges, because we can output them without any problems.
+ int nextLeaf = findLeaf();
+ if (nextLeaf == -1)
+ break; // No more leafs left, we're done here.
+ output.append(_moves.takeAt(nextLeaf));
+ // Now there might be new leaf edges: any move that had the input of the previously found
+ // leaf as an output, so loop around.
+ }
+
+ while (!_moves.isEmpty()) {
+ // We're now left with one or more cycles.
+ // Step one: break the/a cycle.
+ _moves.removeFirst();
+ // Step two: find the other edges of the cycle, starting with the one of that is now a leaf.
+ while (!_moves.isEmpty()) {
+ int nextLeaf = findLeaf();
+ if (nextLeaf == -1)
+ break; // We're done with this cycle.
+ Move m = _moves.takeAt(nextLeaf);
+ // Step three: get the edges from the cycle and turn it into a swap
+ m.needsSwap = true;
+ output.append(m);
+ // Because we took out a leaf, find the next one.
+ }
+ // We're done with the cycle, let's see if there are more.
+ }
+
+ _moves = output;
+}
+
+int MoveMapping::findLeaf() const
+{
+ for (int i = 0, e = _moves.size(); i != e; ++i) {
+ // Take an edge from the list...
+ const Temp *target = _moves.at(i).to;
+ // ... and see if its target is used as a source...
+ bool targetUsedAsSource = false;
+ for (int j = 0; j != e; ++j) {
+ if (i == j)
+ continue;
- output += swaps;
+ Expr *source = _moves.at(j).from;
+ if (const Temp *sourceTemp = source->asTemp()) {
+ if (overlappingStorage(*target, *sourceTemp)) {
+ targetUsedAsSource = true;
+ break;
+ }
+ }
+ }
+ // ... if not, we have a leaf edge ...
+ if (!targetUsedAsSource)
+ return i;
+ // .. otherwise we try the next one.
+ }
- Q_ASSERT(todo.isEmpty());
- Q_ASSERT(delayed.isEmpty());
- qSwap(_moves, output);
+ return -1; // No leaf found
}
QList<IR::Move *> MoveMapping::insertMoves(BasicBlock *bb, IR::Function *function, bool atEnd) const
@@ -5695,60 +5796,12 @@ void MoveMapping::dump() const
}
}
-MoveMapping::Action MoveMapping::schedule(const Move &m, QList<Move> &todo, QList<Move> &delayed,
- QList<Move> &output, QList<Move> &swaps) const
-{
- const Moves usages = sourceUsages(m.to, todo) + sourceUsages(m.to, delayed);
- for (const Move &dependency : usages) {
- if (!output.contains(dependency)) {
- if (delayed.contains(dependency)) {
- // We have a cycle! Break it by swapping instead of assigning.
- if (DebugMoveMapping) {
- delayed += m;
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream out(&buf);
- IRPrinter printer(&out);
- out<<"we have a cycle! temps:" << endl;
- for (const Move &m : qAsConst(delayed)) {
- out<<"\t";
- printer.print(m.to);
- out<<" <- ";
- printer.print(m.from);
- out<<endl;
- }
- qDebug("%s", buf.data().constData());
- delayed.removeOne(m);
- }
-
- 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;
-}
-
// References:
// [Wimmer1] C. Wimmer and M. Franz. Linear Scan Register Allocation on SSA Form. In Proceedings of
-// CGO’10, ACM Press, 2010
+// CGO'10, ACM Press, 2010
// [Wimmer2] C. Wimmer and H. Mossenbock. Optimized Interval Splitting in a Linear Scan Register
// Allocator. In Proceedings of the ACM/USENIX International Conference on Virtual
-// Execution Environments, pages 132–141. ACM Press, 2005.
+// Execution Environments, pages 132-141. ACM Press, 2005.
// [Briggs] P. Briggs, K.D. Cooper, T.J. Harvey, and L.T. Simpson. Practical Improvements to the
// Construction and Destruction of Static Single Assignment Form.
// [Appel] A.W. Appel. Modern Compiler Implementation in Java. Second edition, Cambridge