Compress temp usage in the interpreter.
authorErik Verbruggen <erik.verbruggen@digia.com>
Tue, 20 Nov 2012 08:35:32 +0000 (09:35 +0100)
committerLars Knoll <lars.knoll@digia.com>
Tue, 20 Nov 2012 11:05:41 +0000 (12:05 +0100)
Uses a variation on linear scan register allocation as the algorithm. As
it depends on liveness analysis, keep that data around after codegen is
finished (in IR::Stmt::Data). Added clean-up code for it in the
IR::Function destructor.

Change-Id: If3636648efbafcc1df469a24aaa885e21e6a2f16
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
moth/qv4isel_moth.cpp
qv4codegen.cpp
qv4ir.cpp

index 318b227..3dc747c 100644 (file)
@@ -22,6 +22,111 @@ static unsigned toValueOrTemp(IR::Expr *e, Instr::ValueOrTemp &vot)
     }
 }
 
+class CompressTemps: public IR::StmtVisitor, IR::ExprVisitor
+{
+public:
+    void run(IR::Function *function)
+    {
+        _active.reserve(function->tempCount);
+        _localCount = function->locals.size();
+        int maxUsed = _localCount;
+
+        foreach (IR::BasicBlock *block, function->basicBlocks) {
+            foreach (IR::Stmt *s, block->statements) {
+                _currentStatement = s;
+                if (s->d)
+                    s->accept(this);
+            }
+            maxUsed = std::max(maxUsed, _nextFree + _localCount);
+        }
+
+        function->tempCount = maxUsed;
+    }
+
+private:
+    virtual void visitConst(IR::Const *) {}
+    virtual void visitString(IR::String *) {}
+    virtual void visitRegExp(IR::RegExp *) {}
+    virtual void visitName(IR::Name *) {}
+    virtual void visitClosure(IR::Closure *) {}
+    virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); }
+    virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); }
+    virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); }
+    virtual void visitMember(IR::Member *e) { e->base->accept(this); }
+    virtual void visitExp(IR::Exp *s) { s->expr->accept(this); }
+    virtual void visitEnter(IR::Enter *) {}
+    virtual void visitLeave(IR::Leave *) {}
+    virtual void visitJump(IR::Jump *) {}
+    virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); }
+    virtual void visitRet(IR::Ret *s) { s->expr->accept(this); }
+
+    virtual void visitTemp(IR::Temp *e) {
+        if (e->index < 0)
+            return;
+        if (e->index < _localCount) // don't optimise locals yet.
+            return;
+
+        e->index = remap(e->index - _localCount) + _localCount;
+    }
+
+    virtual void visitCall(IR::Call *e) {
+        e->base->accept(this);
+        for (IR::ExprList *it = e->args; it; it = it->next)
+            it->expr->accept(this);
+    }
+
+    virtual void visitNew(IR::New *e) {
+        e->base->accept(this);
+        for (IR::ExprList *it = e->args; it; it = it->next)
+            it->expr->accept(this);
+    }
+
+    virtual void visitMove(IR::Move *s) {
+        s->target->accept(this);
+        s->source->accept(this);
+    }
+
+    int remap(int tempIndex) {
+        for (ActiveTemps::const_iterator i = _active.begin(), ei = _active.end(); i < ei; ++i) {
+            if (i->first == tempIndex) {
+                return i->second;
+            }
+        }
+
+        int firstFree = expireOld();
+        if (_nextFree <= firstFree)
+            _nextFree = firstFree + 1;
+        _active.prepend(qMakePair(tempIndex, firstFree));
+        return firstFree;
+    }
+
+    int expireOld() {
+        const QBitArray &liveIn = _currentStatement->d->liveIn;
+        QBitArray inUse(_nextFree);
+        int i = 0;
+        while (i < _active.size()) {
+            const QPair<int, int> &p = _active[i];
+            if (liveIn[p.first + _localCount]) {
+                inUse[p.second] = true;
+                ++i;
+            } else {
+                _active.remove(i);
+            }
+        }
+        for (int i = 0, ei = inUse.size(); i < ei; ++i)
+            if (!inUse[i])
+                return i;
+        return _nextFree;
+    }
+
+private:
+    typedef QVector<QPair<int, int> > ActiveTemps;
+    ActiveTemps _active;
+    IR::Stmt *_currentStatement;
+    int _localCount;
+    int _nextFree;
+};
+
 } // anonymous namespace
 
 InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module * /*module*/,
@@ -38,6 +143,8 @@ void InstructionSelection::operator()(IR::Function *function)
 {
     qSwap(_function, function);
 
+    CompressTemps().run(_function);
+
     _function->code = VME::exec;
     _function->codeData = _ccode;
 
index 7a367ca..30def8f 100644 (file)
@@ -1432,7 +1432,17 @@ void Codegen::linearize(IR::Function *function)
 
             if (IR::BasicBlock *bb = leader.value(s)) {
                 qout << endl;
-                qout << 'L' << bb->index << ':' << endl;
+                QByteArray str;
+                str.append('L');
+                str.append(QByteArray::number(bb->index));
+                str.append(':');
+                for (int i = 66 - str.length(); i; --i)
+                    str.append(' ');
+                qout << str;
+                qout << "// predecessor blocks:";
+                foreach (IR::BasicBlock *in, bb->in)
+                    qout << " L" << in->index;
+                qout << endl;
             }
             IR::Stmt *n = (i + 1) < code.size() ? code.at(i + 1) : 0;
             if (n && s->asJump() && s->asJump()->target == leader.value(n)) {
@@ -1467,7 +1477,7 @@ void Codegen::linearize(IR::Function *function)
             //        }
 
             if (! s->d->liveOut.isEmpty()) {
-                qout << " // lives:";
+                qout << " // lives out:";
                 for (int i = 0; i < s->d->liveOut.size(); ++i) {
                     if (s->d->liveOut.testBit(i))
                         qout << " %" << i;
@@ -1487,13 +1497,6 @@ void Codegen::linearize(IR::Function *function)
         qout << "}" << endl
              << endl;
     }
-
-#ifndef QV4_NO_LIVENESS
-    foreach (IR::BasicBlock *block, function->basicBlocks) {
-        foreach (IR::Stmt *s, block->statements)
-            s->destroyData();
-    }
-#endif
 }
 
 IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
index 811c802..3a84f64 100644 (file)
--- a/qv4ir.cpp
+++ b/qv4ir.cpp
@@ -387,6 +387,12 @@ Function *Module::newFunction(const QString &name)
 
 Function::~Function()
 {
+    // destroy the Stmt::Data blocks manually, because memory pool cleanup won't
+    // call the Stmt destructors.
+    foreach (IR::BasicBlock *b, basicBlocks)
+        foreach (IR::Stmt *s, b->statements)
+            s->destroyData();
+
     qDeleteAll(basicBlocks);
 }