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.cpp689
1 files changed, 545 insertions, 144 deletions
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
index 8c5f2e30bb..d67b88b718 100644
--- a/src/qml/compiler/qv4ssa.cpp
+++ b/src/qml/compiler/qv4ssa.cpp
@@ -5,36 +5,28 @@
**
** This file is part of the QtQml module of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
@@ -81,8 +73,10 @@ Q_GLOBAL_STATIC_WITH_ARGS(QTextStream, qout, (stderr, QIODevice::WriteOnly));
void showMeTheCode(IR::Function *function)
{
static bool showCode = !qgetenv("QV4_SHOW_IR").isNull();
- if (showCode)
+ if (showCode) {
IRPrinter(&qout).print(function);
+ qout << endl;
+ }
}
class ProcessedBlocks
@@ -337,7 +331,9 @@ class DominatorTree
{
enum {
DebugDominatorFrontiers = 0,
- DebugImmediateDominators = 0
+ DebugImmediateDominators = 0,
+
+ DebugCodeCanUseLotsOfCpu = 0
};
typedef int BasicBlockIndex;
@@ -493,11 +489,6 @@ class DominatorTree
}
}
-#if defined(SHOW_SSA)
- for (int i = 0; i < nodes.size(); ++i)
- qDebug("\tL%d: ancestor = %d, semi = %d, samedom = %d", i, ancestor[i], semi[i], samedom[i]);
-#endif // SHOW_SSA
-
for (int i = 1; i < d->N; ++i) {
BasicBlockIndex n = d->vertex[i];
Q_ASSERT(n != InvalidBasicBlockIndex);
@@ -617,26 +608,28 @@ public:
}
}
-#if !defined(QT_NO_DEBUG) && defined(CAN_TAKE_LOSTS_OF_TIME)
- foreach (BasicBlock *n, nodes) {
- const BasicBlockSet &fBlocks = DF[n->index];
- for (BasicBlockSet::const_iterator it = fBlocks.begin(), eit = fBlocks.end(); it != eit; ++it) {
- BasicBlock *fBlock = *it;
- Q_ASSERT(!dominates(n, fBlock) || fBlock == n);
- bool hasDominatedSucc = false;
- foreach (BasicBlock *succ, fBlock->in) {
- if (dominates(n, succ)) {
- hasDominatedSucc = true;
- break;
+ if (DebugDominatorFrontiers && DebugCodeCanUseLotsOfCpu) {
+ foreach (BasicBlock *n, function->basicBlocks()) {
+ if (n->isRemoved())
+ continue;
+ const BasicBlockSet &fBlocks = DF[n->index()];
+ for (BasicBlockSet::const_iterator it = fBlocks.begin(), eit = fBlocks.end(); it != eit; ++it) {
+ BasicBlock *fBlock = *it;
+ Q_ASSERT(!dominates(n, fBlock) || fBlock == n);
+ bool hasDominatedSucc = false;
+ foreach (BasicBlock *succ, fBlock->in) {
+ if (dominates(n, succ)) {
+ hasDominatedSucc = true;
+ break;
+ }
}
+ if (!hasDominatedSucc) {
+ qout << fBlock << " in DF[" << n->index() << "] has no dominated predecessors" << endl;
+ }
+ Q_ASSERT(hasDominatedSucc);
}
- if (!hasDominatedSucc) {
- qout << fBlock << " in DF[" << n->index << "] has no dominated predecessors" << endl;
- }
- Q_ASSERT(hasDominatedSucc);
}
}
-#endif // !QT_NO_DEBUG
}
const BasicBlockSet &dominatorFrontier(BasicBlock *n) const {
@@ -669,7 +662,7 @@ public:
}
}
- void updateImmediateDominator(BasicBlock *bb, BasicBlock *newDominator)
+ void setImmediateDominator(BasicBlock *bb, BasicBlock *newDominator)
{
Q_ASSERT(bb->index() >= 0);
Q_ASSERT(!newDominator || newDominator->index() >= 0);
@@ -680,7 +673,33 @@ public:
idom.resize(function->basicBlockCount(), InvalidBasicBlockIndex);
}
- idom[bb->index()] = newDominator ? newDominator->index() : 0;
+ const BasicBlockIndex newIdx = newDominator ? newDominator->index() : InvalidBasicBlockIndex;
+ if (DebugImmediateDominators)
+ qDebug() << "Setting idom of" << bb->index() << "from" << idom[bb->index()] << "to" << newIdx;
+ idom[bb->index()] = newIdx;
+ }
+
+ void collectSiblings(BasicBlock *node, BasicBlockSet &siblings)
+ {
+ siblings.insert(node);
+ const BasicBlockIndex dominator = idom[node->index()];
+ if (dominator == InvalidBasicBlockIndex)
+ return;
+ for (size_t i = 0, ei = idom.size(); i != ei; ++i) {
+ if (idom[i] == dominator) {
+ BasicBlock *bb = function->basicBlock(i);
+ if (!bb->isRemoved())
+ siblings.insert(bb);
+ }
+ }
+ }
+
+ void recalculateIDoms(const BasicBlockSet &nodes, BasicBlock *limit = 0)
+ {
+ const BasicBlockIndex limitIndex = limit ? limit->index() : InvalidBasicBlockIndex;
+ BasicBlockSet todo(nodes), postponed(function);
+ while (!todo.empty())
+ recalculateIDom(*todo.begin(), todo, postponed, limitIndex);
}
bool dominates(BasicBlock *dominator, BasicBlock *dominated) const {
@@ -801,6 +820,97 @@ private:
return depth;
}
+
+ // The immediate-dominator recalculation is used when edges are removed from the CFG. See
+ // [Ramalingam] for a description. Note that instead of calculating the priority, a recursive
+ // algorithm is used: when recalculating the immediate dominator of a node by looking for the
+ // least-common ancestor, and a node is hit that also needs recalculation, a recursive call
+ // is done to calculate that nodes immediate dominator first.
+ //
+ // Note that this simplified algorithm cannot cope with back-edges. It only works for
+ // non-looping edges (which is our use-case).
+ void recalculateIDom(BasicBlock *node, BasicBlockSet &todo, BasicBlockSet &postponed, BasicBlockIndex limit) {
+ Q_ASSERT(!postponed.contains(node));
+ Q_ASSERT(todo.contains(node));
+ todo.remove(node);
+
+ if (node->in.size() == 1) {
+ // Special case: if the node has only one incoming edge, then that is the immediate
+ // dominator.
+ setImmediateDominator(node, node->in.first());
+ return;
+ }
+
+ std::vector<BasicBlockIndex> prefix;
+ prefix.reserve(32);
+
+ for (int i = 0, ei = node->in.size(); i != ei; ++i) {
+ BasicBlock *in = node->in.at(i);
+ if (node == in) // back-edge to self
+ continue;
+ if (dominates(node->index(), in->index())) // a known back-edge
+ continue;
+
+ if (prefix.empty()) {
+ calculatePrefix(node, in, prefix, todo, postponed, limit);
+
+ if (!prefix.empty()) {
+ std::reverse(prefix.begin(), prefix.end());
+ Q_ASSERT(!prefix.empty());
+ Q_ASSERT(prefix.front() == limit || limit == InvalidBasicBlockIndex);
+ }
+ } else {
+ std::vector<BasicBlockIndex> anotherPrefix;
+ anotherPrefix.reserve(prefix.size());
+ calculatePrefix(node, in, anotherPrefix, todo, postponed, limit);
+
+ if (!anotherPrefix.empty())
+ commonPrefix(prefix, anotherPrefix);
+ }
+ }
+
+ Q_ASSERT(!prefix.empty());
+ idom[node->index()] = prefix.back();
+ }
+
+ void calculatePrefix(BasicBlock *node, BasicBlock *in, std::vector<BasicBlockIndex> &prefix, BasicBlockSet &todo, BasicBlockSet &postponed, BasicBlockIndex limit)
+ {
+ for (BasicBlockIndex it = in->index(); it != InvalidBasicBlockIndex; it = idom[it]) {
+ prefix.push_back(it);
+ if (it == limit)
+ return;
+ BasicBlock *n = function->basicBlock(it);
+ if (postponed.contains(n)) { // possible back-edge, bail out.
+ prefix.clear();
+ return;
+ }
+ if (todo.contains(n)) {
+ postponed.insert(node);
+ recalculateIDom(n, todo, postponed, limit);
+ postponed.remove(node);
+ }
+ }
+ }
+
+ // Calculate the LCA (Least Common Ancestor) by finding the longest common prefix between two
+ // dominator chains. Note that "anotherPrefix" has the node's immediate dominator first, while
+ // "bestPrefix" has it last (meaning: is in reverse order). The reason for this is that removing
+ // nodes from "bestPrefix" is cheaper because it's done at the end of the vector, while
+ // reversing all "anotherPrefix" nodes would take unnecessary time.
+ static void commonPrefix(std::vector<BasicBlockIndex> &bestPrefix, const std::vector<BasicBlockIndex> &anotherPrefix)
+ {
+ const size_t anotherSize = anotherPrefix.size();
+ size_t minLen = qMin(bestPrefix.size(), anotherPrefix.size());
+ while (minLen != 0) {
+ --minLen;
+ if (bestPrefix[minLen] == anotherPrefix[anotherSize - minLen - 1]) {
+ ++minLen;
+ break;
+ }
+ }
+ if (minLen != bestPrefix.size())
+ bestPrefix.erase(bestPrefix.begin() + minLen, bestPrefix.end());
+ }
};
class VariableCollector: public StmtVisitor, ExprVisitor {
@@ -1659,7 +1769,7 @@ class StatementWorklist
public:
StatementWorklist(IR::Function *function)
: theFunction(function)
- , stmts(function->statementCount(), 0)
+ , stmts(function->statementCount(), static_cast<Stmt *>(0))
, worklist(function->statementCount(), false)
, worklistSize(0)
, replaced(function->statementCount(), Stmt::InvalidId)
@@ -1684,6 +1794,9 @@ public:
void reset()
{
+ worklist.assign(worklist.size(), false);
+ worklistSize = 0;
+
foreach (Stmt *s, stmts) {
if (!s)
continue;
@@ -2102,10 +2215,14 @@ class TypeInference: public StmtVisitor, public ExprVisitor
DiscoveredType type;
bool fullyTyped;
- TypingResult(const DiscoveredType &type = DiscoveredType())
- : type(type)
- , fullyTyped(type.type != UnknownType)
- {}
+ TypingResult(const DiscoveredType &type = DiscoveredType()) {
+#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 6
+ // avoid optimization bug in gcc 4.6.3 armhf
+ ((int volatile &) this->type.type) = type.type;
+#endif
+ this->type = type;
+ fullyTyped = type.type != UnknownType;
+ }
explicit TypingResult(MemberExpressionResolver memberResolver)
: type(memberResolver)
, fullyTyped(true)
@@ -2845,37 +2962,47 @@ protected:
void splitCriticalEdges(IR::Function *f, DominatorTree &df, StatementWorklist &worklist, DefUses &defUses)
{
- foreach (BasicBlock *bb, f->basicBlocks()) {
- if (bb->isRemoved())
+ foreach (BasicBlock *toBB, f->basicBlocks()) {
+ if (toBB->isRemoved())
continue;
- if (bb->in.size() < 2)
+ if (toBB->in.size() < 2)
continue;
- for (int inIdx = 0, eInIdx = bb->in.size(); inIdx != eInIdx; ++inIdx) {
- BasicBlock *inBB = bb->in[inIdx];
- if (inBB->out.size() < 2)
+ for (int inIdx = 0, eInIdx = toBB->in.size(); inIdx != eInIdx; ++inIdx) {
+ BasicBlock *fromBB = toBB->in[inIdx];
+ if (fromBB->out.size() < 2)
continue;
// We found a critical edge.
// create the basic block:
- BasicBlock *newBB = f->newBasicBlock(bb->catchBlock);
+ BasicBlock *newBB = f->newBasicBlock(toBB->catchBlock);
Jump *s = f->NewStmt<Jump>();
worklist.registerNewStatement(s);
defUses.registerNewStatement(s);
- s->init(bb);
+ s->init(toBB);
newBB->appendStatement(s);
// rewire the old outgoing edge
- int outIdx = inBB->out.indexOf(bb);
- inBB->out[outIdx] = newBB;
- newBB->in.append(inBB);
+ int outIdx = fromBB->out.indexOf(toBB);
+ fromBB->out[outIdx] = newBB;
+ newBB->in.append(fromBB);
// rewire the old incoming edge
- bb->in[inIdx] = newBB;
- newBB->out.append(bb);
+ toBB->in[inIdx] = newBB;
+ newBB->out.append(toBB);
+
+ BasicBlock *container = 0;
+ for (container = fromBB->containingGroup(); container; container = container->containingGroup()) {
+ if (container == toBB || container == toBB->containingGroup())
+ break;
+ }
+ if (container == 0)
+ container = fromBB->containingGroup();
+
+ newBB->setContainingGroup(container);
// patch the terminator
- Stmt *terminator = inBB->terminator();
+ Stmt *terminator = fromBB->terminator();
if (Jump *j = terminator->asJump()) {
Q_ASSERT(outIdx == 0);
j->target = newBB;
@@ -2892,8 +3019,21 @@ void splitCriticalEdges(IR::Function *f, DominatorTree &df, StatementWorklist &w
Q_ASSERT(!"Unknown terminator!");
}
+// qDebug() << "splitting edge" << fromBB->index() << "->" << toBB->index()
+// << "by inserting block" << newBB->index();
+
// Set the immediate dominator of the new block to inBB
- df.updateImmediateDominator(newBB, inBB);
+ df.setImmediateDominator(newBB, fromBB);
+
+ bool toNeedsNewIdom = true;
+ foreach (BasicBlock *bb, toBB->in) {
+ if (bb != newBB && !df.dominates(toBB, bb)) {
+ toNeedsNewIdom = false;
+ break;
+ }
+ }
+ if (toNeedsNewIdom)
+ df.setImmediateDominator(toBB, newBB);
}
}
}
@@ -3507,78 +3647,113 @@ namespace {
/// This function removes the basic-block from the function's list, unlinks any uses and/or defs,
/// and removes unreachable staements from the worklist, so that optimiseSSA won't consider them
/// anymore.
-/// Important: this assumes that there are no critical edges in the control-flow graph!
-void purgeBB(BasicBlock *bb, IR::Function *func, DefUses &defUses, StatementWorklist &W,
- DominatorTree &df)
+void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUses,
+ StatementWorklist &W, DominatorTree &dt)
{
- // TODO: after the change above: if we keep on detaching the block from predecessors or
- // successors, update the DominatorTree too.
+ struct Util {
+ static void removeIncomingEdge(BasicBlock *from, BasicBlock *to, DefUses &defUses, StatementWorklist &W)
+ {
+ int idx = to->in.indexOf(from);
+ if (idx == -1)
+ return;
+
+ to->in.remove(idx);
+ foreach (Stmt *outStmt, to->statements()) {
+ if (!outStmt)
+ continue;
+ if (Phi *phi = outStmt->asPhi()) {
+ if (Temp *t = phi->d->incoming[idx]->asTemp()) {
+ defUses.removeUse(phi, *t);
+ W += defUses.defStmt(*t);
+ }
+ phi->d->incoming.remove(idx);
+ W += phi;
+ } else {
+ break;
+ }
+ }
+ }
+
+ static bool isReachable(BasicBlock *bb, const DominatorTree &dt)
+ {
+ foreach (BasicBlock *in, bb->in) {
+ if (in->isRemoved())
+ continue;
+ if (dt.dominates(bb, in)) // a back-edge, not interesting
+ continue;
+ return true;
+ }
+
+ return false;
+ }
+ };
// don't purge blocks that are entry points for catch statements. They might not be directly
// connected, but are required anyway
- if (bb->isExceptionHandler())
+ if (to->isExceptionHandler())
return;
- QVector<BasicBlock *> toPurge;
- toPurge.reserve(8);
- toPurge.append(bb);
+ // First, unlink the edge
+ from->out.removeOne(to);
+ Util::removeIncomingEdge(from, to, defUses, W);
- while (!toPurge.isEmpty()) {
- bb = toPurge.first();
- toPurge.removeFirst();
+ BasicBlockSet siblings;
+ siblings.init(func);
- if (bb->isRemoved())
- continue;
+ // Check if the target is still reachable...
+ if (Util::isReachable(to, dt)) { // yes, recalculate the immediate dominator, and we're done.
+ dt.collectSiblings(to, siblings);
+ } else {
+ // The target is unreachable, so purge it:
+ QVector<BasicBlock *> toPurge;
+ toPurge.reserve(8);
+ toPurge.append(to);
+ while (!toPurge.isEmpty()) {
+ BasicBlock *bb = toPurge.first();
+ toPurge.removeFirst();
- // unlink all incoming edges
- foreach (BasicBlock *in, bb->in) {
- int idx = in->out.indexOf(bb);
- if (idx != -1)
- in->out.remove(idx);
- }
+ if (bb->isRemoved())
+ continue;
- // unlink all outgoing edges, including "arguments" to phi statements
- foreach (BasicBlock *out, bb->out) {
- int idx = out->in.indexOf(bb);
- if (idx != -1) {
- out->in.remove(idx);
- foreach (Stmt *outStmt, out->statements()) {
- if (!outStmt)
- continue;
- if (Phi *phi = outStmt->asPhi()) {
- if (Temp *t = phi->d->incoming[idx]->asTemp()) {
- defUses.removeUse(phi, *t);
- W += defUses.defStmt(*t);
- }
- phi->d->incoming.remove(idx);
- W += phi;
- } else
- break;
- }
+ // unlink all incoming edges
+ foreach (BasicBlock *in, bb->in) {
+ int idx = in->out.indexOf(bb);
+ if (idx != -1)
+ in->out.remove(idx);
}
- if (out->in.isEmpty()) {
- // if a successor has no incoming edges after unlinking the current basic block, then
- // it is unreachable, and can be purged too
- toPurge.append(out);
- } else if (out->in.size() == 1) {
- // if the successor now has only one incoming edge, we that edge is the new
- // immediate dominator
- df.updateImmediateDominator(out, out->in.first());
+ // unlink all outgoing edges, including "arguments" to phi statements
+ foreach (BasicBlock *out, bb->out) {
+ if (out->isRemoved())
+ continue;
+
+ Util::removeIncomingEdge(bb, out, defUses, W);
+
+ if (Util::isReachable(out, dt)) {
+ dt.collectSiblings(out, siblings);
+ } else {
+ // if a successor has no incoming edges after unlinking the current basic block,
+ // then it is unreachable, and can be purged too
+ toPurge.append(out);
+ }
}
- }
- // unlink all defs/uses from the statements in the basic block
- foreach (Stmt *s, bb->statements()) {
- if (!s)
- continue;
+ // unlink all defs/uses from the statements in the basic block
+ foreach (Stmt *s, bb->statements()) {
+ if (!s)
+ continue;
- W += defUses.removeDefUses(s);
- W -= s;
- }
+ W += defUses.removeDefUses(s);
+ W -= s;
+ }
- func->removeBasicBlock(bb);
+ siblings.remove(bb);
+ dt.setImmediateDominator(bb, 0);
+ func->removeBasicBlock(bb);
+ }
}
+
+ dt.recalculateIDoms(siblings);
}
bool tryOptimizingComparison(Expr *&expr)
@@ -3762,6 +3937,7 @@ void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df)
// constant propagation:
if (Const *sourceConst = m->source->asConst()) {
+ Q_ASSERT(sourceConst->type != UnknownType);
replaceUses(targetTemp, sourceConst, W);
defUses.removeDef(*targetTemp);
W.remove(s);
@@ -3826,7 +4002,8 @@ void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df)
doneSomething = true;
break;
case OpUPlus:
- constOperand->type = unop->type;
+ if (unop->type != UnknownType)
+ constOperand->type = unop->type;
doneSomething = true;
break;
case OpCompl:
@@ -3971,10 +4148,10 @@ void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df)
W.registerNewStatement(jump);
if (convertToValue(constantCondition).toBoolean()) {
jump->target = cjump->iftrue;
- purgeBB(cjump->iffalse, function, defUses, W, df);
+ unlink(cjump->parent, cjump->iffalse, function, defUses, W, df);
} else {
jump->target = cjump->iffalse;
- purgeBB(cjump->iftrue, function, defUses, W, df);
+ unlink(cjump->parent, cjump->iftrue, function, defUses, W, df);
}
W.replace(s, jump);
@@ -4333,6 +4510,199 @@ private:
std::vector<int> tempForLocal;
};
+class CloneBasicBlock: protected IR::StmtVisitor, protected CloneExpr
+{
+public:
+ BasicBlock *operator()(IR::BasicBlock *originalBlock)
+ {
+ block = new BasicBlock(originalBlock->function, 0);
+
+ foreach (Stmt *s, originalBlock->statements()) {
+ s->accept(this);
+ clonedStmt->location = s->location;
+ }
+
+ return block;
+ }
+
+protected:
+ virtual void visitExp(Exp *stmt)
+ { clonedStmt = block->EXP(clone(stmt->expr)); }
+
+ virtual void visitMove(Move *stmt)
+ { clonedStmt = block->MOVE(clone(stmt->target), clone(stmt->source)); }
+
+ virtual void visitJump(Jump *stmt)
+ { clonedStmt = block->JUMP(stmt->target); }
+
+ virtual void visitCJump(CJump *stmt)
+ { clonedStmt = block->CJUMP(clone(stmt->cond), stmt->iftrue, stmt->iffalse); }
+
+ virtual void visitRet(Ret *stmt)
+ { clonedStmt = block->RET(clone(stmt->expr)); }
+
+ virtual void visitPhi(Phi *stmt)
+ {
+ Phi *phi = block->function->NewStmt<Phi>();
+ clonedStmt = phi;
+
+ phi->targetTemp = clone(stmt->targetTemp);
+ phi->d = new Stmt::Data;
+ foreach (Expr *in, stmt->d->incoming)
+ phi->d->incoming.append(clone(in));
+ block->appendStatement(phi);
+ }
+
+private:
+ IR::Stmt *clonedStmt;
+};
+
+// Loop-peeling is done by unfolding the loop once. The "original" loop basic blocks stay where they
+// are, and a copy of the loop is placed after it. Special care is taken while copying the loop body:
+// by having the copies of the basic-blocks point to the same nodes as the "original" basic blocks,
+// updating the immediate dominators is easy: if the edge of a copied basic-block B points to a
+// block C that has also been copied, set the immediate dominator of B to the corresponding
+// immediate dominator of C. Finally, for any node outside the loop that gets a new edge attached,
+// the immediate dominator has to be re-calculated.
+class LoopPeeling
+{
+ DominatorTree &dt;
+
+public:
+ LoopPeeling(DominatorTree &dt)
+ : dt(dt)
+ {}
+
+ void run(const QVector<LoopDetection::LoopInfo *> &loops)
+ {
+ foreach (LoopDetection::LoopInfo *loopInfo, loops)
+ peelLoop(loopInfo);
+ }
+
+private:
+ // All copies have their outgoing edges pointing to the same successor block as the originals.
+ // For each copied block, check where the outgoing edges point to. If it's a block inside the
+ // (original) loop, rewire it to the corresponding copy. Otherwise, which is when it points
+ // out of the loop, leave it alone.
+ // As an extra, collect all edges that point out of the copied loop, because the targets need
+ // to have their immediate dominator rechecked.
+ void rewire(BasicBlock *newLoopBlock, const QVector<BasicBlock *> &from, const QVector<BasicBlock *> &to, QVector<BasicBlock *> &loopExits)
+ {
+ for (int i = 0, ei = newLoopBlock->out.size(); i != ei; ++i) {
+ BasicBlock *&out = newLoopBlock->out[i];
+ const int idx = from.indexOf(out);
+ if (idx == -1) {
+ if (!loopExits.contains(out))
+ loopExits.append(out);
+ } else {
+ out->in.removeOne(newLoopBlock);
+ BasicBlock *newTo = to.at(idx);
+ newTo->in.append(newLoopBlock);
+ out = newTo;
+
+ Stmt *terminator = newLoopBlock->terminator();
+ if (Jump *jump = terminator->asJump()) {
+ Q_ASSERT(i == 0);
+ jump->target = newTo;
+ } else if (CJump *cjump = terminator->asCJump()) {
+ Q_ASSERT(i == 0 || i == 1);
+ if (i == 0)
+ cjump->iftrue = newTo;
+ else
+ cjump->iffalse = newTo;
+ }
+ }
+ }
+ }
+
+ void peelLoop(LoopDetection::LoopInfo *loop)
+ {
+ CloneBasicBlock clone;
+
+ LoopDetection::LoopInfo unpeeled(*loop);
+ unpeeled.loopHeader = clone(unpeeled.loopHeader);
+ unpeeled.loopHeader->setContainingGroup(loop->loopHeader->containingGroup());
+ unpeeled.loopHeader->markAsGroupStart(true);
+ for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) {
+ BasicBlock *&bodyBlock = unpeeled.loopBody[i];
+ bodyBlock = clone(bodyBlock);
+ bodyBlock->setContainingGroup(unpeeled.loopHeader);
+ Q_ASSERT(bodyBlock->statementCount() == loop->loopBody[i]->statementCount());
+ }
+
+ // The cloned blocks will have no incoming edges, but they do have outgoing ones (copying
+ // the terminators will automatically insert that edge). The blocks where the originals
+ // pointed to will have an extra incoming edge from the copied blocks.
+
+ foreach (BasicBlock *in, loop->loopHeader->in) {
+ if (unpeeled.loopHeader != in // this can happen for really tight loops (where there are no body blocks). This is a back-edge in that case.
+ && !unpeeled.loopBody.contains(in) // if the edge is not coming from within the copied set, leave it alone
+ && !dt.dominates(loop->loopHeader, in)) // an edge coming from within the loop (so a back-edge): this is handled when rewiring all outgoing edges
+ continue;
+
+ unpeeled.loopHeader->in.append(in);
+ loop->loopHeader->in.removeOne(in);
+
+ Stmt *terminator = in->terminator();
+ if (Jump *jump = terminator->asJump()) {
+ jump->target = unpeeled.loopHeader;
+ in->out[0] = unpeeled.loopHeader;
+ } else if (CJump *cjump = terminator->asCJump()) {
+ if (cjump->iftrue == loop->loopHeader) {
+ cjump->iftrue = unpeeled.loopHeader;
+ Q_ASSERT(in->out[0] == loop->loopHeader);
+ in->out[0] = unpeeled.loopHeader;
+ } else if (cjump->iffalse == loop->loopHeader) {
+ cjump->iffalse = unpeeled.loopHeader;
+ Q_ASSERT(in->out[1] == loop->loopHeader);
+ in->out[1] = unpeeled.loopHeader;
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+ }
+
+ QVector<BasicBlock *> loopExits;
+ loopExits.reserve(8);
+ loopExits.append(unpeeled.loopHeader);
+
+ IR::Function *f = unpeeled.loopHeader->function;
+ rewire(unpeeled.loopHeader, loop->loopBody, unpeeled.loopBody, loopExits);
+ f->addBasicBlock(unpeeled.loopHeader);
+ for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) {
+ BasicBlock *bodyBlock = unpeeled.loopBody.at(i);
+ rewire(bodyBlock, loop->loopBody, unpeeled.loopBody, loopExits);
+ f->addBasicBlock(bodyBlock);
+ }
+
+ // The original loop is now peeled off, and won't jump back to the loop header. Meaning, it
+ // is not a loop anymore, so unmark it.
+ loop->loopHeader->markAsGroupStart(false);
+ foreach (BasicBlock *bb, loop->loopBody)
+ bb->setContainingGroup(loop->loopHeader->containingGroup());
+
+ // calculate the idoms in a separate loop, because addBasicBlock in the previous loop will
+ // set the block index, which in turn is used by the dominator tree.
+ for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) {
+ BasicBlock *bodyBlock = unpeeled.loopBody.at(i);
+ BasicBlock *idom = dt.immediateDominator(loop->loopBody.at(i));
+ const int idx = loop->loopBody.indexOf(idom);
+ if (idom == loop->loopHeader)
+ idom = unpeeled.loopHeader;
+ else if (idx != -1)
+ idom = unpeeled.loopBody.at(idx);
+ Q_ASSERT(idom);
+ dt.setImmediateDominator(bodyBlock, idom);
+ }
+
+ BasicBlockSet siblings(f);
+ foreach (BasicBlock *bb, loopExits)
+ dt.collectSiblings(bb, siblings);
+
+ dt.recalculateIDoms(siblings, loop->loopHeader);
+ }
+};
+
static void verifyCFG(IR::Function *function)
{
if (!DoVerification)
@@ -4692,7 +5062,7 @@ Optimizer::Optimizer(IR::Function *function)
, inSSA(false)
{}
-void Optimizer::run(QQmlEnginePrivate *qmlEngine)
+void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool peelLoops)
{
#if defined(SHOW_SSA)
qout << "##### NOW IN FUNCTION " << (function->name ? qPrintable(*function->name) : "anonymous!")
@@ -4717,6 +5087,31 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine)
// Calculate the dominator tree:
DominatorTree df(function);
+
+ {
+ // This is in a separate scope, because loop-peeling doesn't (yet) update the LoopInfo
+ // calculated by the LoopDetection. So by putting it in a separate scope, it is not
+ // available after peeling.
+
+ LoopDetection loopDetection(df);
+ loopDetection.run(function);
+ showMeTheCode(function);
+// cfg2dot(function, loopDetection.allLoops());
+
+ if (peelLoops) {
+ QVector<LoopDetection::LoopInfo *> innerLoops = loopDetection.innermostLoops();
+ LoopPeeling(df).run(innerLoops);
+
+// cfg2dot(function, loopDetection.allLoops());
+ showMeTheCode(function);
+ if (!innerLoops.isEmpty())
+ verifyImmediateDominators(df, function);
+ }
+ }
+
+ verifyCFG(function);
+ verifyNoPointerSharing(function);
+
df.computeDF();
verifyCFG(function);
@@ -4731,39 +5126,37 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine)
// qout << "Cleaning up phi nodes..." << endl;
cleanupPhis(defUses);
-// showMeTheCode(function);
+ showMeTheCode(function);
StatementWorklist worklist(function);
-// qout << "Running type inference..." << endl;
- TypeInference(qmlEngine, defUses).run(worklist);
-// showMeTheCode(function);
+ if (doTypeInference) {
+// qout << "Running type inference..." << endl;
+ TypeInference(qmlEngine, defUses).run(worklist);
+ showMeTheCode(function);
-// qout << "Doing reverse inference..." << endl;
- ReverseInference(defUses).run(function);
-// showMeTheCode(function);
-
-// qout << "Doing type propagation..." << endl;
- TypePropagation(defUses).run(function, worklist);
-// showMeTheCode(function);
- verifyNoPointerSharing(function);
+// qout << "Doing reverse inference..." << endl;
+ ReverseInference(defUses).run(function);
+// showMeTheCode(function);
- // Transform the CFG into edge-split SSA.
-// qout << "Starting edge splitting..." << endl;
- splitCriticalEdges(function, df, worklist, defUses);
-// showMeTheCode(function);
+// qout << "Doing type propagation..." << endl;
+ TypePropagation(defUses).run(function, worklist);
+// showMeTheCode(function);
+ verifyNoPointerSharing(function);
+ }
static bool doOpt = qgetenv("QV4_NO_OPT").isEmpty();
if (doOpt) {
// qout << "Running SSA optimization..." << endl;
worklist.reset();
optimizeSSA(worklist, defUses, df);
-// showMeTheCode(function);
+ showMeTheCode(function);
+
+ verifyImmediateDominators(df, function);
+ verifyCFG(function);
}
-// qout << "Doing block merging..." << endl;
-// mergeBasicBlocks(function);
-// showMeTheCode(function);
+ verifyNoPointerSharing(function);
// Basic-block cycles that are unreachable (i.e. for loops in a then-part where the
// condition is calculated to be always false) are not yet removed. This will choke the
@@ -4772,13 +5165,19 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine)
cleanupBasicBlocks(function);
// showMeTheCode(function);
- LoopDetection(df).run(function);
- showMeTheCode(function);
+ // Transform the CFG into edge-split SSA.
+// qout << "Starting edge splitting..." << endl;
+ splitCriticalEdges(function, df, worklist, defUses);
+// showMeTheCode(function);
+
+ verifyImmediateDominators(df, function);
+ verifyCFG(function);
// qout << "Doing block scheduling..." << endl;
// df.dumpImmediateDominators();
startEndLoops = BlockScheduler(function, df).go();
showMeTheCode(function);
+// cfg2dot(function);
#ifndef QT_NO_DEBUG
checkCriticalEdges(function->basicBlocks());
@@ -5063,3 +5462,5 @@ MoveMapping::Action MoveMapping::schedule(const Move &m, QList<Move> &todo, QLis
// Construction and Destruction of Static Single Assignment Form.
// [Appel] A.W. Appel. Modern Compiler Implementation in Java. Second edition, Cambridge
// University Press.
+// [Ramalingam] G. Ramalingam and T. Reps. An Incremental Algorithm for Maintaining the Dominator
+// Tree of a Reducible Flowgraph.