diff options
author | Erik Verbruggen <[email protected]> | 2013-09-01 15:45:53 +0200 |
---|---|---|
committer | The Qt Project <[email protected]> | 2013-09-13 11:41:23 +0200 |
commit | 9c82c105a1886473ca144b802ce9f5bec01e35e8 (patch) | |
tree | 2caeb5945e2b60f49ba046ec50a45c0bec4f2f7d | |
parent | 0ef673efe8bf381e1aa0202752deac27e86ada53 (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 <[email protected]>
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 7 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_masm.cpp | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_moth.cpp | 29 | ||||
-rw-r--r-- | src/qml/compiler/qv4regalloc.cpp | 137 | ||||
-rw-r--r-- | src/qml/compiler/qv4ssa.cpp | 276 | ||||
-rw-r--r-- | src/qml/compiler/qv4ssa_p.h | 46 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 4 |
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); |