aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2014-01-16 21:52:48 +0100
committerSimon Hausmann <simon.hausmann@digia.com>2014-01-16 21:53:57 +0100
commit7030adff1869e850a7b983e88d7a773d5d594886 (patch)
treea3e8ef3ba29c9ea34ee00038595aaa1fe00afe2c /src
parent5ba070d305572a7e427a62042967d737bd4791ac (diff)
parent6ccb9f8f04ea257520e518b25999907c6a8421e1 (diff)
Merge remote-tracking branch 'origin/release' into stable
Diffstat (limited to 'src')
-rw-r--r--src/imports/dialogs/DefaultFileDialog.qml15
-rw-r--r--src/imports/widgets/qquickqfiledialog.cpp4
-rw-r--r--src/qml/compiler/qv4regalloc.cpp162
-rw-r--r--src/qml/compiler/qv4ssa.cpp148
-rw-r--r--src/qml/compiler/qv4ssa_p.h6
-rw-r--r--src/qml/jsruntime/qv4context.cpp6
-rw-r--r--src/qml/jsruntime/qv4context_p.h1
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp3
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp11
9 files changed, 222 insertions, 134 deletions
diff --git a/src/imports/dialogs/DefaultFileDialog.qml b/src/imports/dialogs/DefaultFileDialog.qml
index 0a5eabddf2..627a719f60 100644
--- a/src/imports/dialogs/DefaultFileDialog.qml
+++ b/src/imports/dialogs/DefaultFileDialog.qml
@@ -53,7 +53,13 @@ AbstractFileDialog {
currentPathField.visible = false
}
}
- onFolderChanged: view.model.folder = folder
+ onFolderChanged: {
+ var str = new String(folder)
+ if (str.indexOf("qrc:") === 0)
+ folder = "file:" + str.slice(4)
+ if (view.model.folder != folder)
+ view.model.folder = folder
+ }
property real __textX: titleBar.height
property SystemPalette __palette
@@ -61,12 +67,15 @@ AbstractFileDialog {
property int __lastClickedIdx: -1
function __dirDown(path) {
- view.model.folder = path
+ view.model.folder = "file://" + path
__lastClickedIdx = -1
__selectedIndices = []
}
function __dirUp() {
- view.model.folder = view.model.parentFolder
+ if (view.model.parentFolder == "")
+ view.model.folder = "file:///"
+ else
+ view.model.folder = view.model.parentFolder
__lastClickedIdx = -1
__selectedIndices = []
}
diff --git a/src/imports/widgets/qquickqfiledialog.cpp b/src/imports/widgets/qquickqfiledialog.cpp
index c3991b4f3c..403577fabe 100644
--- a/src/imports/widgets/qquickqfiledialog.cpp
+++ b/src/imports/widgets/qquickqfiledialog.cpp
@@ -172,6 +172,10 @@ void QFileDialogHelper::setFilter() {
m_dialog.setFileMode(QFileDialog::FileMode(QPlatformFileDialogHelper::options()->fileMode()));
m_dialog.setOptions((QFileDialog::Options)((int)(QPlatformFileDialogHelper::options()->options())));
m_dialog.setAcceptMode(QFileDialog::AcceptMode(QPlatformFileDialogHelper::options()->acceptMode()));
+
+ const QUrl initialDirectory = QPlatformFileDialogHelper::options()->initialDirectory();
+ if (initialDirectory.isValid() && initialDirectory.isLocalFile())
+ m_dialog.setDirectory(initialDirectory.toLocalFile());
}
bool QFileDialogHelper::show(Qt::WindowFlags f, Qt::WindowModality m, QWindow *parent) {
diff --git a/src/qml/compiler/qv4regalloc.cpp b/src/qml/compiler/qv4regalloc.cpp
index 93ecdb2602..3521d0c27a 100644
--- a/src/qml/compiler/qv4regalloc.cpp
+++ b/src/qml/compiler/qv4regalloc.cpp
@@ -124,7 +124,7 @@ public:
return _defs[t].isPhiTarget;
}
- QList<int> calls() const { return _calls; }
+ const QList<int> &calls() const { return _calls; }
QList<Temp> hints(const Temp &t) const { return _hints[t]; }
void addHint(const Temp &t, int physicalRegister)
{
@@ -658,13 +658,14 @@ using namespace QT_PREPEND_NAMESPACE(QQmlJS);
namespace {
class ResolutionPhase: protected StmtVisitor, protected ExprVisitor {
- QVector<LifeTimeInterval> _intervals;
+ const QVector<LifeTimeInterval> &_intervals;
+ QVector<const LifeTimeInterval *> _unprocessed;
Function *_function;
#if !defined(QT_NO_DEBUG)
RegAllocInfo *_info;
#endif
const QHash<V4IR::Temp, int> &_assignedSpillSlots;
- QHash<V4IR::Temp, LifeTimeInterval> _intervalForTemp;
+ QHash<V4IR::Temp, const LifeTimeInterval *> _intervalForTemp;
const QVector<int> &_intRegs;
const QVector<int> &_fpRegs;
@@ -672,8 +673,8 @@ class ResolutionPhase: protected StmtVisitor, protected ExprVisitor {
QVector<Move *> _loads;
QVector<Move *> _stores;
- QHash<BasicBlock *, QList<LifeTimeInterval> > _liveAtStart;
- QHash<BasicBlock *, QList<LifeTimeInterval> > _liveAtEnd;
+ QHash<BasicBlock *, QList<const LifeTimeInterval *> > _liveAtStart;
+ QHash<BasicBlock *, QList<const LifeTimeInterval *> > _liveAtEnd;
public:
ResolutionPhase(const QVector<LifeTimeInterval> &intervals, Function *function, RegAllocInfo *info,
@@ -691,6 +692,13 @@ public:
#if defined(QT_NO_DEBUG)
Q_UNUSED(info)
#endif
+
+ _unprocessed.resize(_intervals.size());
+ for (int i = 0, ei = _intervals.size(); i != ei; ++i)
+ _unprocessed[i] = &_intervals[i];
+
+ _liveAtStart.reserve(function->basicBlocks.size());
+ _liveAtEnd.reserve(function->basicBlocks.size());
}
void run() {
@@ -704,6 +712,7 @@ private:
{
foreach (BasicBlock *bb, _function->basicBlocks) {
QVector<Stmt *> newStatements;
+ newStatements.reserve(bb->statements.size() + 7);
bool seenFirstNonPhiStmt = false;
for (int i = 0, ei = bb->statements.size(); i != ei; ++i) {
@@ -754,22 +763,22 @@ private:
}
- void activate(const LifeTimeInterval &i)
+ void activate(const LifeTimeInterval *i)
{
- Q_ASSERT(!i.isFixedInterval());
- _intervalForTemp[i.temp()] = i;
+ Q_ASSERT(!i->isFixedInterval());
+ _intervalForTemp[i->temp()] = i;
- if (i.reg() != LifeTimeInterval::Invalid) {
+ if (i->reg() != LifeTimeInterval::Invalid) {
// check if we need to generate spill/unspill instructions
- if (i.start() == _currentStmt->id) {
- if (i.isSplitFromInterval()) {
- int pReg = platformRegister(i);
- _loads.append(generateUnspill(i.temp(), pReg));
+ if (i->start() == _currentStmt->id) {
+ if (i->isSplitFromInterval()) {
+ int pReg = platformRegister(*i);
+ _loads.append(generateUnspill(i->temp(), pReg));
} else {
- int pReg = platformRegister(i);
- int spillSlot = _assignedSpillSlots.value(i.temp(), -1);
+ int pReg = platformRegister(*i);
+ int spillSlot = _assignedSpillSlots.value(i->temp(), -1);
if (spillSlot != -1)
- _stores.append(generateSpill(spillSlot, i.temp().type, pReg));
+ _stores.append(generateSpill(spillSlot, i->temp().type, pReg));
}
}
}
@@ -779,37 +788,37 @@ private:
{
if (Phi *phi = _currentStmt->asPhi()) {
// for phi nodes, only activate the range belonging to that node
- for (int it = 0, eit = _intervals.size(); it != eit; ++it) {
- const LifeTimeInterval &i = _intervals.at(it);
- if (i.start() > _currentStmt->id)
+ for (int it = 0, eit = _unprocessed.size(); it != eit; ++it) {
+ const LifeTimeInterval *i = _unprocessed.at(it);
+ if (i->start() > _currentStmt->id)
break;
- if (i.temp() == *phi->targetTemp) {
+ if (i->temp() == *phi->targetTemp) {
activate(i);
- _intervals.remove(it);
+ _unprocessed.remove(it);
break;
}
}
return;
}
- while (!_intervals.isEmpty()) {
- const LifeTimeInterval &i = _intervals.first();
- if (i.start() > _currentStmt->id)
+ while (!_unprocessed.isEmpty()) {
+ const LifeTimeInterval *i = _unprocessed.first();
+ if (i->start() > _currentStmt->id)
break;
activate(i);
- _intervals.removeFirst();
+ _unprocessed.removeFirst();
}
}
void cleanOldIntervals()
{
const int id = _currentStmt->id;
- QMutableHashIterator<Temp, LifeTimeInterval> it(_intervalForTemp);
+ QMutableHashIterator<Temp, const LifeTimeInterval *> it(_intervalForTemp);
while (it.hasNext()) {
- const LifeTimeInterval &i = it.next().value();
- if (i.end() < id || i.isFixedInterval())
+ const LifeTimeInterval *i = it.next().value();
+ if (i->end() < id || i->isFixedInterval())
it.remove();
}
}
@@ -817,19 +826,15 @@ private:
void resolve()
{
foreach (BasicBlock *bb, _function->basicBlocks) {
- foreach (BasicBlock *bbOut, bb->out) {
-#ifdef DEBUG_REGALLOC
- Optimizer::showMeTheCode(_function);
-#endif // DEBUG_REGALLOC
-
+ foreach (BasicBlock *bbOut, bb->out)
resolveEdge(bb, bbOut);
- }
}
}
void resolveEdge(BasicBlock *predecessor, BasicBlock *successor)
{
#ifdef DEBUG_REGALLOC
+ Optimizer::showMeTheCode(_function);
qDebug() << "Resolving edge" << predecessor->index << "->" << successor->index;
#endif // DEBUG_REGALLOC
@@ -848,17 +853,21 @@ private:
Q_ASSERT(successorStart > 0);
- foreach (const LifeTimeInterval &it, _liveAtStart[successor]) {
- bool lifeTimeHole = false;
- if (it.end() < successorStart)
+ foreach (const LifeTimeInterval *it, _liveAtStart[successor]) {
+ if (it->end() < successorStart)
continue;
+
+ bool lifeTimeHole = false;
+ bool isPhiTarget = false;
Expr *moveFrom = 0;
- if (it.start() == successorStart) {
+
+ if (it->start() == successorStart) {
foreach (Stmt *s, successor->statements) {
if (!s || s->id < 1)
continue;
if (Phi *phi = s->asPhi()) {
- if (*phi->targetTemp == it.temp()) {
+ if (*phi->targetTemp == it->temp()) {
+ isPhiTarget = true;
Expr *opd = phi->d->incoming[successor->in.indexOf(predecessor)];
if (opd->asConst()) {
moveFrom = opd;
@@ -866,12 +875,12 @@ private:
Temp *t = opd->asTemp();
Q_ASSERT(t);
- foreach (const LifeTimeInterval &it2, _liveAtEnd[predecessor]) {
- if (it2.temp() == *t
- && it2.reg() != LifeTimeInterval::Invalid
- && it2.covers(predecessorEnd)) {
+ foreach (const LifeTimeInterval *it2, _liveAtEnd[predecessor]) {
+ if (it2->temp() == *t
+ && it2->reg() != LifeTimeInterval::Invalid
+ && it2->covers(predecessorEnd)) {
moveFrom = createTemp(Temp::PhysicalRegister,
- platformRegister(it2), t->type);
+ platformRegister(*it2), t->type);
break;
}
}
@@ -886,18 +895,18 @@ private:
}
}
} else {
- foreach (const LifeTimeInterval &predIt, _liveAtEnd[predecessor]) {
- if (predIt.temp() == it.temp()) {
- if (predIt.reg() != LifeTimeInterval::Invalid
- && predIt.covers(predecessorEnd)) {
- moveFrom = createTemp(Temp::PhysicalRegister, platformRegister(predIt),
- predIt.temp().type);
+ foreach (const LifeTimeInterval *predIt, _liveAtEnd[predecessor]) {
+ if (predIt->temp() == it->temp()) {
+ if (predIt->reg() != LifeTimeInterval::Invalid
+ && predIt->covers(predecessorEnd)) {
+ moveFrom = createTemp(Temp::PhysicalRegister, platformRegister(*predIt),
+ predIt->temp().type);
} else {
- int spillSlot = _assignedSpillSlots.value(predIt.temp(), -1);
+ int spillSlot = _assignedSpillSlots.value(predIt->temp(), -1);
if (spillSlot == -1)
lifeTimeHole = true;
else
- moveFrom = createTemp(Temp::StackSlot, spillSlot, predIt.temp().type);
+ moveFrom = createTemp(Temp::StackSlot, spillSlot, predIt->temp().type);
}
break;
}
@@ -906,20 +915,20 @@ private:
if (!moveFrom) {
Q_UNUSED(lifeTimeHole);
#if !defined(QT_NO_DEBUG)
- Q_ASSERT(!_info->isPhiTarget(it.temp()) || it.isSplitFromInterval() || lifeTimeHole);
- if (_info->def(it.temp()) != successorStart && !it.isSplitFromInterval()) {
+ Q_ASSERT(!_info->isPhiTarget(it->temp()) || it->isSplitFromInterval() || lifeTimeHole);
+ if (_info->def(it->temp()) != successorStart && !it->isSplitFromInterval()) {
const int successorEnd = successor->statements.last()->id;
const int idx = successor->in.indexOf(predecessor);
- foreach (const Use &use, _info->uses(it.temp())) {
+ foreach (const Use &use, _info->uses(it->temp())) {
if (use.pos == static_cast<unsigned>(successorStart)) {
// only check the current edge, not all other possible ones. This is
// important for phi nodes: they have uses that are only valid when
// coming in over a specific edge.
foreach (Stmt *s, successor->statements) {
if (Phi *phi = s->asPhi()) {
- Q_ASSERT(it.temp().index != phi->targetTemp->index);
+ Q_ASSERT(it->temp().index != phi->targetTemp->index);
Q_ASSERT(phi->d->incoming[idx]->asTemp() == 0
- || it.temp().index != phi->d->incoming[idx]->asTemp()->index);
+ || it->temp().index != phi->d->incoming[idx]->asTemp()->index);
} else {
// TODO: check that the first non-phi statement does not use
// the temp.
@@ -938,13 +947,18 @@ private:
}
Temp *moveTo;
- if (it.reg() == LifeTimeInterval::Invalid || !it.covers(successorStart)) {
- int spillSlot = _assignedSpillSlots.value(it.temp(), -1);
+ if (it->reg() == LifeTimeInterval::Invalid || !it->covers(successorStart)) {
+ if (!isPhiTarget) // if it->temp() is a phi target, skip it.
+ continue;
+ const int spillSlot = _assignedSpillSlots.value(it->temp(), -1);
if (spillSlot == -1)
continue; // it has a life-time hole here.
- moveTo = createTemp(Temp::StackSlot, spillSlot, it.temp().type);
+ moveTo = createTemp(Temp::StackSlot, spillSlot, it->temp().type);
} else {
- moveTo = createTemp(Temp::PhysicalRegister, platformRegister(it), it.temp().type);
+ moveTo = createTemp(Temp::PhysicalRegister, platformRegister(*it), it->temp().type);
+ const int spillSlot = _assignedSpillSlots.value(it->temp(), -1);
+ if (isPhiTarget && spillSlot != -1)
+ mapping.add(moveFrom, createTemp(Temp::StackSlot, spillSlot, it->temp().type));
}
// add move to mapping
@@ -1005,10 +1019,10 @@ protected:
if (t->kind != Temp::VirtualRegister)
return;
- const LifeTimeInterval &i = _intervalForTemp[*t];
- Q_ASSERT(i.isValid());
- if (i.reg() != LifeTimeInterval::Invalid && i.covers(_currentStmt->id)) {
- int pReg = platformRegister(i);
+ const LifeTimeInterval *i = _intervalForTemp[*t];
+ Q_ASSERT(i->isValid());
+ if (i->reg() != LifeTimeInterval::Invalid && i->covers(_currentStmt->id)) {
+ int pReg = platformRegister(*i);
t->kind = Temp::PhysicalRegister;
t->index = pReg;
} else {
@@ -1078,7 +1092,7 @@ void RegisterAllocator::run(Function *function, const Optimizer &opt)
{
QTextStream qout(stdout, QIODevice::WriteOnly);
qout << "Ranges:" << endl;
- QList<LifeTimeInterval> intervals = _unhandled;
+ QVector<LifeTimeInterval> intervals = _unhandled;
std::sort(intervals.begin(), intervals.end(), LifeTimeInterval::lessThanForTemp);
foreach (const LifeTimeInterval &r, intervals) {
r.dump(qout);
@@ -1114,7 +1128,7 @@ void RegisterAllocator::run(Function *function, const Optimizer &opt)
#endif // DEBUG_REGALLOC
}
-static inline LifeTimeInterval createFixedInterval(int reg, bool isFP)
+static inline LifeTimeInterval createFixedInterval(int reg, bool isFP, int rangeCount)
{
Temp t;
t.init(Temp::PhysicalRegister, reg, 0);
@@ -1123,6 +1137,7 @@ static inline LifeTimeInterval createFixedInterval(int reg, bool isFP)
i.setTemp(t);
i.setReg(reg);
i.setFixedInterval(true);
+ i.reserveRanges(rangeCount);
return i;
}
@@ -1131,12 +1146,13 @@ void RegisterAllocator::prepareRanges()
const int regCount = _normalRegisters.size();
_fixedRegisterRanges.resize(regCount);
for (int reg = 0; reg < regCount; ++reg)
- _fixedRegisterRanges[reg] = createFixedInterval(reg, false);
+ _fixedRegisterRanges[reg] = createFixedInterval(reg, false, _info->calls().size());
const int fpRegCount = _fpRegisters.size();
_fixedFPRegisterRanges.resize(fpRegCount);
- for (int fpReg = 0; fpReg < fpRegCount; ++fpReg)
- _fixedFPRegisterRanges[fpReg] = createFixedInterval(fpReg, true);
+ for (int fpReg = 0; fpReg < fpRegCount; ++fpReg) {
+ _fixedFPRegisterRanges[fpReg] = createFixedInterval(fpReg, true, _info->calls().size());
+ }
foreach (int callPosition, _info->calls()) {
for (int reg = 0; reg < regCount; ++reg)
@@ -1448,9 +1464,9 @@ int RegisterAllocator::nextIntersection(const LifeTimeInterval &current,
return -1;
for (int currentEnd = currentRanges.size(); currentIt < currentEnd; ++currentIt) {
- const LifeTimeInterval::Range &currentRange = currentRanges[currentIt];
+ const LifeTimeInterval::Range currentRange = currentRanges.at(currentIt);
for (int anotherIt = anotherItStart, anotherEnd = anotherRanges.size(); anotherIt < anotherEnd; ++anotherIt) {
- const LifeTimeInterval::Range &anotherRange = anotherRanges[anotherIt];
+ const LifeTimeInterval::Range anotherRange = anotherRanges.at(anotherIt);
if (anotherRange.start > currentRange.end)
break;
int intersectPos = intersectionPosition(currentRange, anotherRange);
@@ -1585,7 +1601,7 @@ void RegisterAllocator::dump() const
{
qout << "Ranges:" << endl;
- QList<LifeTimeInterval> handled = _handled;
+ QVector<LifeTimeInterval> handled = _handled;
std::sort(handled.begin(), handled.end(), LifeTimeInterval::lessThanForTemp);
foreach (const LifeTimeInterval &r, handled) {
r.dump(qout);
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
index 31a1e4cdc4..7113dc7c26 100644
--- a/src/qml/compiler/qv4ssa.cpp
+++ b/src/qml/compiler/qv4ssa.cpp
@@ -308,9 +308,9 @@ public:
BasicBlock *operator*() const
{
if (set.blockNumbers)
- return set.allBlocks[*numberIt];
+ return set.allBlocks.at(*numberIt);
else
- return set.allBlocks[flagIt];
+ return set.allBlocks.at(flagIt);
}
bool operator==(const const_iterator &other) const
@@ -409,7 +409,7 @@ class DominatorTree {
typedef int BasicBlockIndex;
enum { InvalidBasicBlockIndex = -1 };
- const QVector<BasicBlock *> nodes;
+ QVector<BasicBlock *> nodes;
int N;
std::vector<int> dfnum; // BasicBlock index -> dfnum
std::vector<int> vertex;
@@ -472,9 +472,8 @@ class DominatorTree {
#endif // SHOW_SSA
}
- BasicBlockIndex ancestorWithLowestSemi(BasicBlockIndex v) {
- std::vector<BasicBlockIndex> worklist;
- worklist.reserve(vertex.capacity() / 2);
+ BasicBlockIndex ancestorWithLowestSemi(BasicBlockIndex v, std::vector<BasicBlockIndex> &worklist) {
+ worklist.clear();
for (BasicBlockIndex it = v; it != InvalidBasicBlockIndex; it = ancestor[it])
worklist.push_back(it);
@@ -517,6 +516,9 @@ class DominatorTree {
DFS(nodes.first()->index);
Q_ASSERT(N == nodes.size()); // fails with unreachable nodes, but those should have been removed before.
+ std::vector<BasicBlockIndex> worklist;
+ worklist.reserve(vertex.capacity() / 2);
+
for (int i = N - 1; i > 0; --i) {
BasicBlockIndex n = vertex[i];
BasicBlockIndex p = parent[n];
@@ -527,7 +529,7 @@ class DominatorTree {
if (dfnum[v->index] <= dfnum[n])
ss = v->index;
else
- ss = semi[ancestorWithLowestSemi(v->index)];
+ ss = semi[ancestorWithLowestSemi(v->index, worklist)];
if (dfnum[ss] < dfnum[s])
s = ss;
}
@@ -536,7 +538,7 @@ class DominatorTree {
link(p, n);
if (bucket.contains(p)) {
foreach (BasicBlockIndex v, bucket[p]) {
- BasicBlockIndex y = ancestorWithLowestSemi(v);
+ BasicBlockIndex y = ancestorWithLowestSemi(v, worklist);
BasicBlockIndex semi_v = semi[v];
if (semi[y] == semi_v)
idom[v] = semi_v;
@@ -602,7 +604,7 @@ class DominatorTree {
std::vector<BasicBlockIndex> worklist;
worklist.reserve(nodes.size() * 2);
for (int i = 0, ei = nodes.size(); i != ei; ++i) {
- BasicBlockIndex nodeIndex = nodes[i]->index;
+ BasicBlockIndex nodeIndex = nodes.at(i)->index;
worklist.push_back(nodeIndex);
NodeProgress &np = nodeStatus[nodeIndex];
np.children = children[nodeIndex];
@@ -633,7 +635,7 @@ class DominatorTree {
if (np.todo.empty()) {
BasicBlockSet &S = DF[node];
S.init(nodes);
- foreach (BasicBlock *y, nodes[node]->out)
+ foreach (BasicBlock *y, nodes.at(node)->out)
if (idom[y->index] != node)
S.insert(y);
foreach (BasicBlockIndex child, np.children) {
@@ -695,10 +697,6 @@ public:
computeDF();
}
-// QSet<BasicBlock *> operator[](BasicBlock *n) const {
-// return DF[n->index];
-// }
-
const BasicBlockSet &dominatorFrontier(BasicBlock *n) const {
return DF[n->index];
}
@@ -707,14 +705,57 @@ public:
return nodes[idom[bb->index]];
}
+ void dumpImmediateDominators() const
+ {
+ qDebug() << "Immediate dominators for" << idom.size() << "nodes:";
+ for (size_t i = 0, ei = idom.size(); i != ei; ++i)
+ if (idom[i] == InvalidBasicBlockIndex)
+ qDebug("\tnone -> L%d", int(i));
+ else
+ qDebug("\tL%d -> L%d", idom[i], int(i));
+ }
+
+ void updateImmediateDominator(BasicBlock *bb, BasicBlock *newDominator)
+ {
+ Q_ASSERT(bb->index >= 0);
+
+ int blockIndex;
+ if (static_cast<std::vector<BasicBlockIndex>::size_type>(bb->index) >= idom.size()) {
+ // This is a new block, probably introduced by edge splitting. So, we'll have to grow
+ // the array before inserting the immediate dominator.
+ nodes.append(bb);
+ idom.resize(nodes.size(), InvalidBasicBlockIndex);
+ blockIndex = nodes.size() - 1;
+ } else {
+ blockIndex = getBlockIndex(bb);
+ }
+
+ idom[blockIndex] = getBlockIndex(newDominator);
+ }
+
bool dominates(BasicBlock *dominator, BasicBlock *dominated) const {
// The index of the basic blocks might have changed, or the nodes array might have changed,
- // or the block got deleted, so get the index from our copy of the array.
- return dominates(nodes.indexOf(dominator), nodes.indexOf(dominated));
+ // so get the index from our copy of the array.
+ return dominates(getBlockIndex(dominator), getBlockIndex(dominated));
}
private:
+ int getBlockIndex(BasicBlock *bb) const {
+ if (!bb)
+ return InvalidBasicBlockIndex;
+
+ if (bb->index >= 0 && bb->index < nodes.size()) {
+ if (nodes.at(bb->index) == bb)
+ return bb->index;
+ }
+
+ return nodes.indexOf(bb);
+ }
+
bool dominates(BasicBlockIndex dominator, BasicBlockIndex dominated) const {
+ // dominator can be Invalid when the dominated block has no dominator (i.e. the start node)
+ Q_ASSERT(dominated != InvalidBasicBlockIndex);
+
for (BasicBlockIndex it = dominated; it != InvalidBasicBlockIndex; it = idom[it]) {
if (it == dominator)
return true;
@@ -754,6 +795,8 @@ public:
VariableCollector(Function *function)
: variablesCanEscape(function->variablesCanEscape())
{
+ _defsites.reserve(function->tempCount);
+
#if defined(SHOW_SSA)
qout << "Variables collected:" << endl;
#endif // SHOW_SSA
@@ -1018,6 +1061,9 @@ public:
, tempCount(0)
, processed(f->basicBlocks)
{
+ localMapping.reserve(f->tempCount);
+ vregMapping.reserve(f->tempCount);
+ todo.reserve(f->basicBlocks.size());
}
void run() {
@@ -2463,11 +2509,10 @@ protected:
}
};
-void splitCriticalEdges(Function *f)
+void splitCriticalEdges(Function *f, DominatorTree &df)
{
- const QVector<BasicBlock *> oldBBs = f->basicBlocks;
-
- foreach (BasicBlock *bb, oldBBs) {
+ for (int i = 0, ei = f->basicBlocks.size(); i != ei; ++i) {
+ BasicBlock *bb = f->basicBlocks[i];
if (bb->in.size() > 1) {
for (int inIdx = 0, eInIdx = bb->in.size(); inIdx != eInIdx; ++inIdx) {
BasicBlock *inBB = bb->in[inIdx];
@@ -2511,6 +2556,9 @@ void splitCriticalEdges(Function *f)
} else {
Q_ASSERT(!"Unknown terminator!");
}
+
+ // Set the immediate dominator of the new block to inBB
+ df.updateImmediateDominator(newBB, inBB);
}
}
}
@@ -2660,6 +2708,12 @@ public:
showMeTheCode(function);
schedule(function->basicBlocks.first());
+#if defined(SHOW_SSA)
+ qDebug() << "Block sequence:";
+ foreach (BasicBlock *bb, sequence)
+ qDebug("\tL%d", bb->index);
+#endif // SHOW_SSA
+
Q_ASSERT(function->basicBlocks.size() == sequence.size());
function->basicBlocks = sequence;
return loopsStartEnd;
@@ -2895,7 +2949,8 @@ namespace {
/// 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, Function *func, DefUsesCalculator &defUses, QVector<Stmt *> &W)
+void purgeBB(BasicBlock *bb, Function *func, DefUsesCalculator &defUses, QVector<Stmt *> &W,
+ DominatorTree &df)
{
// TODO: change this to mark the block as deleted, but leave it alone so that other references
// won't be dangling pointers.
@@ -2945,11 +3000,15 @@ void purgeBB(BasicBlock *bb, Function *func, DefUsesCalculator &defUses, QVector
}
}
- // if a successor has no incoming edges after unlinking the current basic block, then
- // it is unreachable, and can be purged too
- if (out->in.isEmpty())
+ 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 defs/uses from the statements in the basic block
@@ -3032,7 +3091,7 @@ bool tryOptimizingComparison(Expr *&expr)
}
} // anonymous namespace
-void optimizeSSA(Function *function, DefUsesCalculator &defUses)
+void optimizeSSA(Function *function, DefUsesCalculator &defUses, DominatorTree &df)
{
const bool variablesCanEscape = function->variablesCanEscape();
@@ -3298,10 +3357,10 @@ void optimizeSSA(Function *function, DefUsesCalculator &defUses)
Jump *jump = function->New<Jump>();
if (convertToValue(constantCondition).toBoolean()) {
jump->target = cjump->iftrue;
- purgeBB(cjump->iffalse, function, defUses, W);
+ purgeBB(cjump->iffalse, function, defUses, W, df);
} else {
jump->target = cjump->iffalse;
- purgeBB(cjump->iftrue, function, defUses, W);
+ purgeBB(cjump->iftrue, function, defUses, W, df);
}
*ref[s] = jump;
@@ -3648,9 +3707,6 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine)
if (!function->hasTry && !function->hasWith && !function->module->debugMode && doSSA) {
// qout << "SSA for " << (function->name ? qPrintable(*function->name) : "<anonymous>") << endl;
-// qout << "Starting edge splitting..." << endl;
- splitCriticalEdges(function);
-// showMeTheCode(function);
// Calculate the dominator tree:
DominatorTree df(function->basicBlocks);
@@ -3678,10 +3734,15 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine)
TypePropagation(defUses).run(function);
// showMeTheCode(function);
+ // Transform the CFG into edge-split SSA.
+// qout << "Starting edge splitting..." << endl;
+ splitCriticalEdges(function, df);
+// showMeTheCode(function);
+
static bool doOpt = qgetenv("QV4_NO_OPT").isEmpty();
if (doOpt) {
// qout << "Running SSA optimization..." << endl;
- optimizeSSA(function, defUses);
+ optimizeSSA(function, defUses, df);
// showMeTheCode(function);
}
@@ -3697,6 +3758,7 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine)
// showMeTheCode(function);
// qout << "Doing block scheduling..." << endl;
+// df.dumpImmediateDominators();
startEndLoops = BlockScheduler(function, df).go();
// showMeTheCode(function);
@@ -3837,15 +3899,20 @@ static inline bool overlappingStorage(const Temp &t1, const Temp &t2)
|| (t1.type != DoubleType && t2.type != DoubleType);
}
-int MoveMapping::isUsedAsSource(Expr *e) const
+MoveMapping::Moves MoveMapping::sourceUsages(Expr *e, const Moves &moves)
{
- if (Temp *t = e->asTemp())
- for (int i = 0, ei = _moves.size(); i != ei; ++i)
- if (Temp *from = _moves[i].from->asTemp())
+ Moves usages;
+
+ if (Temp *t = e->asTemp()) {
+ for (int i = 0, ei = moves.size(); i != ei; ++i) {
+ const Move &move = moves[i];
+ if (Temp *from = move.from->asTemp())
if (overlappingStorage(*from, *t))
- return i;
+ usages.append(move);
+ }
+ }
- return -1;
+ return usages;
}
void MoveMapping::add(Expr *from, Temp *to, int id) {
@@ -3924,9 +3991,8 @@ void MoveMapping::dump() const
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];
+ Moves usages = sourceUsages(m.to, todo) + sourceUsages(m.to, delayed);
+ foreach (const Move &dependency, usages) {
if (!output.contains(dependency)) {
if (delayed.contains(dependency)) {
// We have a cycle! Break it by swapping instead of assigning.
diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h
index dcbc83ae65..f90fc5b05b 100644
--- a/src/qml/compiler/qv4ssa_p.h
+++ b/src/qml/compiler/qv4ssa_p.h
@@ -93,6 +93,7 @@ public:
void setFrom(Stmt *from);
void addRange(int from, int to);
Ranges ranges() const { return _ranges; }
+ void reserveRanges(int capacity) { _ranges.reserve(capacity); }
int start() const { return _ranges.first().start; }
int end() const { return _end; }
@@ -165,10 +166,11 @@ class MoveMapping
bool operator==(const Move &other) const
{ return from == other.from && to == other.to; }
};
+ typedef QList<Move> Moves;
- QList<Move> _moves;
+ Moves _moves;
- int isUsedAsSource(Expr *e) const;
+ static Moves sourceUsages(Expr *e, const Moves &moves);
public:
void add(Expr *from, Temp *to, int id = 0);
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index 05a0e66e09..c2c9aefd5f 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -83,9 +83,6 @@ CallContext *ExecutionContext::newCallContext(FunctionObject *function, CallData
c->strictMode = function->strictMode;
c->outer = function->scope;
-#ifndef QT_NO_DEBUG
- assert(c->outer->next != (ExecutionContext *)0x1);
-#endif
c->activation = 0;
@@ -229,9 +226,6 @@ CallContext::CallContext(ExecutionEngine *engine, ObjectRef qml, FunctionObject
strictMode = true;
outer = function->scope;
-#ifndef QT_NO_DEBUG
- assert(outer->next != (ExecutionContext *)0x1);
-#endif
activation = qml.getPointer();
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index 4eb89ad905..334d033193 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -106,7 +106,6 @@ struct Q_QML_EXPORT ExecutionContext : public Managed
ExecutionContext *outer;
Lookup *lookups;
CompiledData::CompilationUnit *compilationUnit;
- ExecutionContext *next; // used in the GC
struct EvalCode
{
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 6e5c137e0b..ce81282aa3 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -133,9 +133,6 @@ void FunctionObject::init(const StringRef n, bool createProto)
type = Type_FunctionObject;
needsActivation = true;
strictMode = false;
-#ifndef QT_NO_DEBUG
- assert(scope->next != (ExecutionContext *)0x1);
-#endif
if (createProto) {
Scoped<Object> proto(s, scope->engine->newObject(scope->engine->protoClass));
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index b8fdb9c9f8..e9d883f248 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -106,8 +106,7 @@ bool qsg_sort_batch_is_valid(Batch *a, Batch *b) { return a->first && !b->first;
bool qsg_sort_batch_increasing_order(Batch *a, Batch *b) { return a->first->order < b->first->order; }
bool qsg_sort_batch_decreasing_order(Batch *a, Batch *b) { return a->first->order > b->first->order; }
-QSGMaterial::Flag QSGMaterial_FullMatrix = (QSGMaterial::Flag) (QSGMaterial::RequiresFullMatrix & ~QSGMaterial::RequiresFullMatrixExceptTranslate);
-QSGMaterial::Flag QSGMaterial_FullExceptTranslate = (QSGMaterial::Flag) (QSGMaterial::RequiresFullMatrixExceptTranslate & ~QSGMaterial::RequiresDeterminant);
+QSGMaterial::Flag QSGMaterial_RequiresFullMatrixBit = (QSGMaterial::Flag) (QSGMaterial::RequiresFullMatrix & ~QSGMaterial::RequiresFullMatrixExceptTranslate);
struct QMatrix4x4_Accessor
{
@@ -1704,12 +1703,14 @@ void Renderer::uploadBatch(Batch *b)
QSGGeometryNode *gn = b->first->node;
QSGGeometry *g = gn->geometry();
- QSGMaterial::Flags flags = gn->activeMaterial()->flags();
+
bool canMerge = (g->drawingMode() == GL_TRIANGLES || g->drawingMode() == GL_TRIANGLE_STRIP)
&& b->positionAttribute >= 0
&& g->indexType() == GL_UNSIGNED_SHORT
- && (flags & (QSGMaterial::CustomCompileStep | QSGMaterial_FullMatrix)) == 0
- && ((flags & QSGMaterial_FullExceptTranslate) == 0 || b->isTranslateOnlyToRoot())
+ && (gn->activeMaterial()->flags() & QSGMaterial::CustomCompileStep) == 0
+ && (((gn->activeMaterial()->flags() & QSGMaterial::RequiresDeterminant) == 0)
+ || (((gn->activeMaterial()->flags() & QSGMaterial_RequiresFullMatrixBit) == 0) && b->isTranslateOnlyToRoot())
+ )
&& b->isSafeToBatch();
b->merged = canMerge;