aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jit/qv4regalloc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jit/qv4regalloc.cpp')
-rw-r--r--src/qml/jit/qv4regalloc.cpp1971
1 files changed, 0 insertions, 1971 deletions
diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp
deleted file mode 100644
index d418b050c4..0000000000
--- a/src/qml/jit/qv4regalloc.cpp
+++ /dev/null
@@ -1,1971 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the V4VM module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** 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 The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtCore/QBuffer>
-#include <QtCore/QDebug>
-#include "qv4regalloc_p.h"
-#include "qv4alloca_p.h"
-#include <private/qv4value_p.h>
-
-#include <algorithm>
-#if defined(Q_CC_MINGW)
-# include <malloc.h>
-#endif
-
-namespace {
-enum { DebugRegAlloc = 0 };
-
-struct Use {
- enum RegisterFlag { MustHaveRegister = 0, CouldHaveRegister = 1 };
- unsigned flag : 1;
- unsigned pos : 31;
-
- Use(): flag(MustHaveRegister), pos(0) {}
- Use(int position, RegisterFlag flag): flag(flag), pos(position)
- { Q_ASSERT(position >= 0); }
-
- bool mustHaveRegister() const { return flag == MustHaveRegister; }
-};
-}
-
-QT_BEGIN_NAMESPACE
-
-Q_DECLARE_TYPEINFO(Use, Q_MOVABLE_TYPE);
-
-using namespace QV4::IR;
-
-namespace QV4 {
-namespace JIT {
-
-namespace {
-class IRPrinterWithPositions: public IRPrinter
-{
- LifeTimeIntervals::Ptr intervals;
- const int positionSize;
-
-public:
- IRPrinterWithPositions(QTextStream *out, const LifeTimeIntervals::Ptr &intervals)
- : IRPrinter(out)
- , intervals(intervals)
- , positionSize(QString::number(intervals->lastPosition()).size())
- {}
-
-protected:
- void addStmtNr(Stmt *s) Q_DECL_OVERRIDE Q_DECL_FINAL
- {
- addJustifiedNr(intervals->positionForStatement(s));
- }
-};
-
-class IRPrinterWithRegisters: public IRPrinterWithPositions
-{
- const RegisterInformation &_registerInformation;
- QHash<int, const RegisterInfo *> _infoForRegularRegister;
- QHash<int, const RegisterInfo *> _infoForFPRegister;
-
-public:
- IRPrinterWithRegisters(QTextStream *out, const LifeTimeIntervals::Ptr &intervals,
- const RegisterInformation &registerInformation)
- : IRPrinterWithPositions(out, intervals)
- , _registerInformation(registerInformation)
- {
- for (int i = 0, ei = _registerInformation.size(); i != ei; ++i)
- if (_registerInformation.at(i).isRegularRegister())
- _infoForRegularRegister.insert(_registerInformation.at(i).reg<int>(),
- &_registerInformation.at(i));
- else
- _infoForFPRegister.insert(_registerInformation.at(i).reg<int>(),
- &_registerInformation.at(i));
- }
-
-protected:
- void visitTemp(Temp *e) Q_DECL_OVERRIDE Q_DECL_FINAL
- {
- switch (e->kind) {
- case Temp::PhysicalRegister: {
- const RegisterInfo *ri = e->type == DoubleType ? _infoForFPRegister.value(e->index, 0)
- : _infoForRegularRegister.value(e->index, 0);
- if (ri) {
- *out << ri->prettyName();
- break;
- }
- Q_FALLTHROUGH();
- }
- default:
- IRPrinterWithPositions::visitTemp(e);
- }
- }
-};
-}
-
-class RegAllocInfo: public IRDecoder
-{
-public:
- typedef QVarLengthArray<Temp, 4> Hints;
-
-private:
- struct Def {
- unsigned valid : 1;
- unsigned canHaveReg : 1;
- unsigned isPhiTarget : 1;
-
- Def(): valid(0), canHaveReg(0), isPhiTarget(0) {}
- Def(bool canHaveReg, bool isPhiTarget)
- : valid(1), canHaveReg(canHaveReg), isPhiTarget(isPhiTarget)
- {
- }
-
- bool isValid() const { return valid != 0; }
- };
-
- IR::LifeTimeIntervals::Ptr _lifeTimeIntervals;
- BasicBlock *_currentBB;
- Stmt *_currentStmt;
- std::vector<Def> _defs;
- std::vector<std::vector<Use> > _uses;
- std::vector<int> _calls;
- std::vector<Hints> _hints;
-
- int usePosition(Stmt *s) const
- {
- int usePos = _lifeTimeIntervals->positionForStatement(s);
- if (usePos == Stmt::InvalidId) // phi-node operand, so:
- usePos = _lifeTimeIntervals->startPosition(_currentBB);
- return usePos;
- }
-
-public:
- RegAllocInfo(): _currentBB(0), _currentStmt(0) {}
-
- void collect(IR::Function *function, const IR::LifeTimeIntervals::Ptr &lifeTimeIntervals)
- {
- _lifeTimeIntervals = lifeTimeIntervals;
- _defs.resize(function->tempCount);
- _uses.resize(function->tempCount);
- _calls.reserve(function->statementCount() / 3);
- _hints.resize(function->tempCount);
-
- for (BasicBlock *bb : function->basicBlocks()) {
- _currentBB = bb;
- for (Stmt *s : bb->statements()) {
- _currentStmt = s;
- visit(s);
- }
- }
- }
-
- const std::vector<Use> &uses(const Temp &t) const
- {
- return _uses.at(t.index);
- }
-
- bool canHaveRegister(const Temp &t) const {
- Q_ASSERT(_defs[t.index].isValid());
- return _defs[t.index].canHaveReg;
- }
- bool isPhiTarget(const Temp &t) const {
- Q_ASSERT(_defs[t.index].isValid());
- return _defs[t.index].isPhiTarget;
- }
-
- const std::vector<int> &calls() const { return _calls; }
- const Hints &hints(const Temp &t) const { return _hints[t.index]; }
- void addHint(const Temp &t, int physicalRegister)
- { addHint(t, Temp::PhysicalRegister, physicalRegister); }
-
- void addHint(const Temp &t, Temp::Kind kind, int hintedIndex)
- {
- Hints &hints = _hints[t.index];
- for (Hints::iterator i = hints.begin(), ei = hints.end(); i != ei; ++i)
- if (i->index == hintedIndex)
- return;
-
- Temp hint;
- hint.init(kind, hintedIndex);
- hints.append(hint);
- }
-
- void dump() const
- {
- if (!DebugRegAlloc)
- return;
-
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinterWithPositions printer(&qout, _lifeTimeIntervals);
-
- qout << "RegAllocInfo:" << endl << "Defs/uses:" << endl;
- for (unsigned t = 0; t < _defs.size(); ++t) {
- const std::vector<Use> &uses = _uses[t];
- if (uses.empty())
- continue;
- qout << "%" << t <<": "
- << " ("
- << (_defs[t].canHaveReg ? "can" : "can NOT")
- << " have a register, and "
- << (_defs[t].isPhiTarget ? "is" : "is NOT")
- << " defined by a phi node), uses at: ";
- for (unsigned i = 0; i < uses.size(); ++i) {
- if (i > 0) qout << ", ";
- qout << uses[i].pos;
- if (uses[i].mustHaveRegister()) qout << "(R)"; else qout << "(S)";
- }
- qout << endl;
- }
-
- qout << "Calls at: ";
- for (unsigned i = 0; i < _calls.size(); ++i) {
- if (i > 0) qout << ", ";
- qout << _calls[i];
- }
- qout << endl;
-
- qout << "Hints:" << endl;
- for (unsigned t = 0; t < _hints.size(); ++t) {
- if (_uses[t].empty())
- continue;
- qout << "\t%" << t << ": ";
- const Hints &hints = _hints[t];
- for (int i = 0; i < hints.size(); ++i) {
- if (i > 0) qout << ", ";
- printer.print(hints[i]);
- }
- qout << endl;
- }
- qDebug("%s", buf.data().constData());
- }
-
-protected: // IRDecoder
- void callBuiltinInvalid(IR::Name *, IR::ExprList *, IR::Expr *) override {}
- void callBuiltinTypeofQmlContextProperty(IR::Expr *, IR::Member::MemberKind, int, IR::Expr *) override {}
- void callBuiltinTypeofMember(IR::Expr *, const QString &, IR::Expr *) override {}
- void callBuiltinTypeofSubscript(IR::Expr *, IR::Expr *, IR::Expr *) override {}
- void callBuiltinTypeofName(const QString &, IR::Expr *) override {}
- void callBuiltinTypeofValue(IR::Expr *, IR::Expr *) override {}
- void callBuiltinDeleteMember(IR::Expr *, const QString &, IR::Expr *) override {}
- void callBuiltinDeleteSubscript(IR::Expr *, IR::Expr *, IR::Expr *) override {}
- void callBuiltinDeleteName(const QString &, IR::Expr *) override {}
- void callBuiltinDeleteValue(IR::Expr *) override {}
- void callBuiltinThrow(IR::Expr *) override {}
- void callBuiltinReThrow() override {}
- void callBuiltinUnwindException(IR::Expr *) override {}
- void callBuiltinPushCatchScope(const QString &) override {};
- void callBuiltinForeachIteratorObject(IR::Expr *, IR::Expr *) override {}
- virtual void callBuiltinForeachNextProperty(IR::Temp *, IR::Temp *) {}
- void callBuiltinForeachNextPropertyname(IR::Expr *, IR::Expr *) override {}
- void callBuiltinPushWithScope(IR::Expr *) override {}
- void callBuiltinPopScope() override {}
- void callBuiltinDeclareVar(bool , const QString &) override {}
- void callBuiltinDefineArray(IR::Expr *, IR::ExprList *) override {}
- void callBuiltinDefineObjectLiteral(IR::Expr *, int, IR::ExprList *, IR::ExprList *, bool) override {}
- void callBuiltinSetupArgumentObject(IR::Expr *) override {}
- void callBuiltinConvertThisToObject() override {}
-
- void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override
- {
- addDef(result);
- if (IR::Temp *tempValue = value->asTemp())
- addUses(tempValue, Use::CouldHaveRegister);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int propertyIndex,
- IR::ExprList *args, IR::Expr *result) override
- {
- Q_UNUSED(propertyIndex)
-
- addDef(result);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args,
- IR::Expr *result) override
- {
- Q_UNUSED(name)
-
- addDef(result);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args,
- IR::Expr *result) override
- {
- addDef(result);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addUses(index->asTemp(), Use::CouldHaveRegister);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void convertType(IR::Expr *source, IR::Expr *target) override
- {
- addDef(target);
-
- bool needsCall = true;
- Use::RegisterFlag sourceReg = Use::CouldHaveRegister;
-
- switch (target->type) {
- case DoubleType:
- switch (source->type) {
- case UInt32Type:
- case SInt32Type:
- case NullType:
- case UndefinedType:
- case BoolType:
- needsCall = false;
- break;
- default:
- break;
- }
- break;
- case BoolType:
- switch (source->type) {
- case UInt32Type:
- sourceReg = Use::MustHaveRegister;
- needsCall = false;
- break;
- case DoubleType:
- case UndefinedType:
- case NullType:
- case SInt32Type:
- needsCall = false;
- break;
- default:
- break;
- }
- break;
- case SInt32Type:
- switch (source->type) {
- case UInt32Type:
- case NullType:
- case UndefinedType:
- case BoolType:
- needsCall = false;
- default:
- break;
- }
- break;
- case UInt32Type:
- switch (source->type) {
- case SInt32Type:
- case NullType:
- case UndefinedType:
- case BoolType:
- needsCall = false;
- default:
- break;
- }
- break;
- default:
- break;
- }
-
- Temp *sourceTemp = source->asTemp();
- if (sourceTemp)
- addUses(sourceTemp, sourceReg);
-
- if (needsCall)
- addCall();
- else if (target->asTemp())
- addHint(target->asTemp(), sourceTemp);
- }
-
- void constructActivationProperty(IR::Name *, IR::ExprList *args, IR::Expr *result) override
- {
- addDef(result);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void constructProperty(IR::Expr *base, const QString &, IR::ExprList *args, IR::Expr *result) override
- {
- addDef(result);
- addUses(base, Use::CouldHaveRegister);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override
- {
- addDef(result);
- addUses(value, Use::CouldHaveRegister);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void loadThisObject(IR::Expr *temp) override
- {
- addDef(temp);
- }
-
- void loadQmlContext(IR::Expr *temp) override
- {
- addDef(temp);
- addCall();
- }
-
- void loadQmlImportedScripts(IR::Expr *temp) override
- {
- addDef(temp);
- addCall();
- }
-
- void loadQmlSingleton(const QString &/*name*/, Expr *temp) override
- {
- Q_UNUSED(temp);
-
- addDef(temp);
- addCall();
- }
-
- void loadConst(IR::Const *sourceConst, Expr *targetTemp) override
- {
- Q_UNUSED(sourceConst);
-
- addDef(targetTemp);
- }
-
- void loadString(const QString &str, Expr *targetTemp) override
- {
- Q_UNUSED(str);
-
- addDef(targetTemp);
- }
-
- void loadRegexp(IR::RegExp *sourceRegexp, Expr *targetTemp) override
- {
- Q_UNUSED(sourceRegexp);
-
- addDef(targetTemp);
- addCall();
- }
-
- void getActivationProperty(const IR::Name *, Expr *temp) override
- {
- addDef(temp);
- addCall();
- }
-
- void setActivationProperty(IR::Expr *source, const QString &) override
- {
- addUses(source->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void initClosure(IR::Closure *closure, Expr *target) override
- {
- Q_UNUSED(closure);
-
- addDef(target);
- addCall();
- }
-
- void getProperty(IR::Expr *base, const QString &, Expr *target) override
- {
- addDef(target);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &) override
- {
- addUses(source->asTemp(), Use::CouldHaveRegister);
- addUses(targetBase->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase,
- IR::Member::MemberKind /*kind*/, int /*propertyIndex*/) override
- {
- addUses(source->asTemp(), Use::CouldHaveRegister);
- addUses(targetBase->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int /*propertyIndex*/) override
- {
- addUses(source->asTemp(), Use::CouldHaveRegister);
- addUses(targetBase->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int /*index*/,
- bool /*captureRequired*/, IR::Expr *target) override
- {
- addDef(target);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void getQObjectProperty(IR::Expr *base, int /*propertyIndex*/, bool /*captureRequired*/,
- bool /*isSingleton*/, int /*attachedPropertiesId*/, IR::Expr *target) override
- {
- addDef(target);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void getElement(IR::Expr *base, IR::Expr *index, Expr *target) override
- {
- addDef(target);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addUses(index->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) override
- {
- addUses(source->asTemp(), Use::CouldHaveRegister);
- addUses(targetBase->asTemp(), Use::CouldHaveRegister);
- addUses(targetIndex->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void copyValue(Expr *source, Expr *target) override
- {
- addDef(target);
- Temp *sourceTemp = source->asTemp();
- if (!sourceTemp)
- return;
- addUses(sourceTemp, Use::CouldHaveRegister);
- Temp *targetTemp = target->asTemp();
- if (targetTemp)
- addHint(targetTemp, sourceTemp);
- }
-
- void swapValues(Expr *, Expr *) override
- {
- // Inserted by the register allocator, so it cannot occur here.
- Q_UNREACHABLE();
- }
-
- void unop(AluOp oper, Expr *source, Expr *target) override
- {
- addDef(target);
-
- bool needsCall = true;
- if (oper == OpNot && source->type == IR::BoolType && target->type == IR::BoolType)
- needsCall = false;
-
-#if 0 // TODO: change masm to generate code
- switch (oper) {
- case OpIfTrue:
- case OpNot:
- case OpUMinus:
- case OpUPlus:
- case OpCompl:
- needsCall = sourceTemp->type & ~NumberType && sourceTemp->type != BoolType;
- break;
-
- case OpIncrement:
- case OpDecrement:
- default:
- Q_UNREACHABLE();
- }
-#endif
-
- IR::Temp *sourceTemp = source->asTemp();
- if (needsCall) {
- if (sourceTemp)
- addUses(sourceTemp, Use::CouldHaveRegister);
- addCall();
- } else {
- if (sourceTemp)
- addUses(sourceTemp, Use::MustHaveRegister);
- }
- }
-
- void binop(AluOp oper, Expr *leftSource, Expr *rightSource, Expr *target) override
- {
- bool needsCall = true;
-
- if (oper == OpStrictEqual || oper == OpStrictNotEqual) {
- bool noCall = leftSource->type == NullType || rightSource->type == NullType
- || leftSource->type == UndefinedType || rightSource->type == UndefinedType
- || leftSource->type == BoolType || rightSource->type == BoolType;
- needsCall = !noCall;
- } else if (leftSource->type == DoubleType && rightSource->type == DoubleType) {
- if (oper == OpMul || oper == OpAdd || oper == OpDiv || oper == OpSub
- || (oper >= OpGt && oper <= OpStrictNotEqual)) {
- needsCall = false;
- }
- } else if (oper == OpBitAnd || oper == OpBitOr || oper == OpBitXor || oper == OpLShift || oper == OpRShift || oper == OpURShift) {
- needsCall = false;
- } else if (oper == OpAdd || oper == OpMul || oper == OpSub
- || (oper >= OpGt && oper <= OpStrictNotEqual)) {
- if (leftSource->type == SInt32Type && rightSource->type == SInt32Type)
- needsCall = false;
- }
-
- addDef(target);
-
- if (needsCall) {
- addUses(leftSource->asTemp(), Use::CouldHaveRegister);
- addUses(rightSource->asTemp(), Use::CouldHaveRegister);
- addCall();
- } else {
- addUses(leftSource->asTemp(), Use::MustHaveRegister);
- addHint(target, leftSource->asTemp());
- addHint(target, rightSource->asTemp());
-
-#if CPU(X86) || CPU(X86_64)
- switch (oper) {
- // The rhs operand can be a memory address
- case OpAdd:
- case OpSub:
- case OpMul:
- case OpDiv:
-#if CPU(X86_64)
- if (leftSource->type == DoubleType || rightSource->type == DoubleType) {
- // well, on 64bit the doubles are mangled, so they must first be loaded in a register and demangled, so...:
- addUses(rightSource->asTemp(), Use::MustHaveRegister);
- break;
- }
- Q_FALLTHROUGH();
-#endif
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- addUses(rightSource->asTemp(), Use::CouldHaveRegister);
- break;
-
- default:
- addUses(rightSource->asTemp(), Use::MustHaveRegister);
- break;
- }
-#else
- addUses(rightSource->asTemp(), Use::MustHaveRegister);
-#endif
- }
- }
-
- void visitJump(IR::Jump *) override {}
- void visitCJump(IR::CJump *s) override
- {
- if (Temp *t = s->cond->asTemp()) {
-#if 0 // TODO: change masm to generate code
- addUses(t, Use::MustHaveRegister);
-#else
- addUses(t, Use::CouldHaveRegister);
- addCall();
-#endif
- } else if (Binop *b = s->cond->asBinop()) {
- binop(b->op, b->left, b->right, 0);
- } else if (s->cond->asConst()) {
- // TODO: SSA optimization for constant condition evaluation should remove this.
- // See also visitCJump() in masm.
- addCall();
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitRet(IR::Ret *s) override
- { addUses(s->expr->asTemp(), Use::CouldHaveRegister); }
-
- void visitPhi(IR::Phi *s) override
- {
- addDef(s->targetTemp, true);
- for (int i = 0, ei = s->incoming.size(); i < ei; ++i) {
- Expr *e = s->incoming.at(i);
- if (Temp *t = e->asTemp()) {
- // The actual use of an incoming value in a phi node is right before the terminator
- // of the other side of the incoming edge.
- const int usePos = _lifeTimeIntervals->positionForStatement(_currentBB->in.at(i)->terminator()) - 1;
- addUses(t, Use::CouldHaveRegister, usePos);
- addHint(s->targetTemp, t);
- addHint(t, s->targetTemp);
- }
- }
- }
-
-protected:
- void callBuiltin(IR::Call *c, IR::Expr *result) override
- {
- addDef(result);
- addUses(c->base, Use::CouldHaveRegister);
- addUses(c->args, Use::CouldHaveRegister);
- addCall();
- }
-
-private:
- void addDef(Expr *e, bool isPhiTarget = false)
- {
- if (!e)
- return;
- Temp *t = e->asTemp();
- if (!t)
- return;
- if (!t || t->kind != Temp::VirtualRegister)
- return;
- Q_ASSERT(!_defs[t->index].isValid());
- bool canHaveReg = true;
- switch (t->type) {
- case QObjectType:
- case VarType:
- case StringType:
- case UndefinedType:
- case NullType:
- canHaveReg = false;
- break;
- default:
- break;
- }
-
- _defs[t->index] = Def(canHaveReg, isPhiTarget);
- }
-
- void addUses(Expr *e, Use::RegisterFlag flag)
- {
- const int usePos = usePosition(_currentStmt);
- addUses(e, flag, usePos);
- }
-
- void addUses(Expr *e, Use::RegisterFlag flag, int usePos)
- {
- Q_ASSERT(usePos > 0);
- if (!e)
- return;
- Temp *t = e->asTemp();
- if (!t)
- return;
- if (t && t->kind == Temp::VirtualRegister)
- _uses[t->index].push_back(Use(usePos, flag));
- }
-
- void addUses(ExprList *l, Use::RegisterFlag flag)
- {
- for (ExprList *it = l; it; it = it->next)
- addUses(it->expr, flag);
- }
-
- void addCall()
- {
- _calls.push_back(usePosition(_currentStmt));
- }
-
- void addHint(Expr *hinted, Temp *hint1, Temp *hint2 = 0)
- {
- if (hinted)
- if (Temp *hintedTemp = hinted->asTemp())
- addHint(hintedTemp, hint1, hint2);
- }
-
- void addHint(Temp *hinted, Temp *hint1, Temp *hint2 = 0)
- {
- if (!hinted || hinted->kind != Temp::VirtualRegister)
- return;
- if (hint1 && hint1->kind == Temp::VirtualRegister && hinted->type == hint1->type)
- addHint(*hinted, Temp::VirtualRegister, hint1->index);
- if (hint2 && hint2->kind == Temp::VirtualRegister && hinted->type == hint2->type)
- addHint(*hinted, Temp::VirtualRegister, hint2->index);
- }
-};
-
-} // JIT namespace
-} // QV4 namespace
-QT_END_NAMESPACE
-
-QT_USE_NAMESPACE
-
-using namespace QT_PREPEND_NAMESPACE(QV4::JIT);
-using namespace QT_PREPEND_NAMESPACE(QV4::IR);
-using namespace QT_PREPEND_NAMESPACE(QV4);
-
-namespace {
-class ResolutionPhase
-{
- Q_DISABLE_COPY(ResolutionPhase)
-
- LifeTimeIntervals::Ptr _intervals;
- QVector<LifeTimeInterval *> _unprocessedReverseOrder;
- IR::Function *_function;
- const std::vector<int> &_assignedSpillSlots;
- std::vector<const LifeTimeInterval *> _liveIntervals;
- const QVector<const RegisterInfo *> &_intRegs;
- const QVector<const RegisterInfo *> &_fpRegs;
-
- Stmt *_currentStmt;
- std::vector<Move *> _loads;
- std::vector<Move *> _stores;
-
- std::vector<std::vector<const LifeTimeInterval *> > _liveAtStart;
- std::vector<std::vector<const LifeTimeInterval *> > _liveAtEnd;
-
-public:
- ResolutionPhase(QVector<LifeTimeInterval *> &&unprocessedReversedOrder,
- const LifeTimeIntervals::Ptr &intervals,
- IR::Function *function,
- const std::vector<int> &assignedSpillSlots,
- const QVector<const RegisterInfo *> &intRegs,
- const QVector<const RegisterInfo *> &fpRegs)
- : _intervals(intervals)
- , _unprocessedReverseOrder(unprocessedReversedOrder)
- , _function(function)
- , _assignedSpillSlots(assignedSpillSlots)
- , _intRegs(intRegs)
- , _fpRegs(fpRegs)
- , _currentStmt(0)
- {
- _liveAtStart.resize(function->basicBlockCount());
- _liveAtEnd.resize(function->basicBlockCount());
- }
-
- void run() {
- renumber();
- if (DebugRegAlloc) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinterWithPositions(&qout, _intervals).print(_function);
- qDebug("%s", buf.data().constData());
- }
- resolve();
- }
-
-private:
- int defPosition(Stmt *s) const
- {
- return usePosition(s) + 1;
- }
-
- int usePosition(Stmt *s) const
- {
- return _intervals->positionForStatement(s);
- }
-
- void renumber()
- {
- QVector<Stmt *> newStatements;
-
- for (BasicBlock *bb : _function->basicBlocks()) {
- _currentStmt = 0;
-
- QVector<Stmt *> statements = bb->statements();
- newStatements.reserve(bb->statements().size() + 7);
- newStatements.erase(newStatements.begin(), newStatements.end());
-
- cleanOldIntervals(_intervals->startPosition(bb));
- addNewIntervals(_intervals->startPosition(bb));
- _liveAtStart[bb->index()] = _liveIntervals;
-
- for (int i = 0, ei = statements.size(); i != ei; ++i) {
- _currentStmt = statements.at(i);
- _loads.clear();
- _stores.clear();
- if (_currentStmt->asTerminator())
- addNewIntervals(usePosition(_currentStmt));
- else
- addNewIntervals(defPosition(_currentStmt));
- visit(_currentStmt);
- for (Move *load : _loads)
- newStatements.append(load);
- if (_currentStmt->asPhi())
- newStatements.prepend(_currentStmt);
- else
- newStatements.append(_currentStmt);
- for (Move *store : _stores)
- newStatements.append(store);
- }
-
- cleanOldIntervals(_intervals->endPosition(bb));
- _liveAtEnd[bb->index()] = _liveIntervals;
-
- if (DebugRegAlloc) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- os << "Intervals live at the start of L" << bb->index() << ":" << endl;
- if (_liveAtStart[bb->index()].empty())
- os << "\t(none)" << endl;
- for (const LifeTimeInterval *i : _liveAtStart.at(bb->index())) {
- os << "\t";
- i->dump(os);
- os << endl;
- }
- os << "Intervals live at the end of L" << bb->index() << ":" << endl;
- if (_liveAtEnd[bb->index()].empty())
- os << "\t(none)" << endl;
- for (const LifeTimeInterval *i : _liveAtEnd.at(bb->index())) {
- os << "\t";
- i->dump(os);
- os << endl;
- }
- qDebug("%s", buf.data().constData());
- }
-
- bb->setStatements(newStatements);
- }
-
- }
-
- const LifeTimeInterval *findLiveInterval(Temp *t) const
- {
- for (const LifeTimeInterval *lti : _liveIntervals) {
- if (lti->temp() == *t)
- return lti;
- }
-
- return nullptr;
- }
-
- void maybeGenerateSpill(Temp *t)
- {
- const LifeTimeInterval *i = findLiveInterval(t);
- if (i->reg() == LifeTimeInterval::InvalidRegister)
- return;
-
- const RegisterInfo *pReg = platformRegister(*i);
- Q_ASSERT(pReg);
- int spillSlot = _assignedSpillSlots[i->temp().index];
- if (spillSlot != RegisterAllocator::InvalidSpillSlot)
- _stores.push_back(generateSpill(spillSlot, i->temp().type, pReg->reg<int>()));
- }
-
- void addNewIntervals(int position)
- {
- if (position == Stmt::InvalidId)
- return;
-
- while (!_unprocessedReverseOrder.isEmpty()) {
- const LifeTimeInterval *i = _unprocessedReverseOrder.constLast();
- if (i->start() > position)
- break;
-
- Q_ASSERT(!i->isFixedInterval());
- auto it = _liveIntervals.begin();
- for (; it != _liveIntervals.end(); ++it) {
- if ((*it)->temp() == i->temp()) {
- *it = i;
- break;
- }
- }
- if (it == _liveIntervals.end())
- _liveIntervals.push_back(i);
-// qDebug() << "-- Activating interval for temp" << i->temp().index;
-
- _unprocessedReverseOrder.removeLast();
- }
- }
-
- void cleanOldIntervals(int position)
- {
- for (size_t it = 0; it != _liveIntervals.size(); ) {
- const LifeTimeInterval *lti = _liveIntervals.at(it);
- if (lti->end() < position || lti->isFixedInterval())
- _liveIntervals.erase(_liveIntervals.begin() + it);
- else
- ++it;
- }
- }
-
- void resolve()
- {
- for (BasicBlock *bb : _function->basicBlocks()) {
- for (BasicBlock *bbOut : bb->out)
- resolveEdge(bb, bbOut);
- }
- }
-
- Phi *findDefPhi(const Temp &t, BasicBlock *bb) const
- {
- for (Stmt *s : bb->statements()) {
- Phi *phi = s->asPhi();
- if (!phi)
- return 0;
-
- if (*phi->targetTemp == t)
- return phi;
- }
-
- Q_UNREACHABLE();
- }
-
- void resolveEdge(BasicBlock *predecessor, BasicBlock *successor)
- {
- if (DebugRegAlloc) {
- qDebug() << "Resolving edge" << predecessor->index() << "->" << successor->index();
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinterWithPositions printer(&qout, _intervals);
- printer.print(predecessor);
- printer.print(successor);
- qDebug("%s", buf.data().constData());
- }
-
- MoveMapping mapping;
-
- const int predecessorEnd = _intervals->endPosition(predecessor);
- Q_ASSERT(predecessorEnd > 0);
-
- int successorStart = _intervals->startPosition(successor);
- Q_ASSERT(successorStart > 0);
-
- for (const LifeTimeInterval *it : _liveAtStart.at(successor->index())) {
- bool isPhiTarget = false;
- Expr *moveFrom = 0;
-
- if (it->start() == successorStart) {
- if (Phi *phi = findDefPhi(it->temp(), successor)) {
- isPhiTarget = true;
- Expr *opd = phi->incoming[successor->in.indexOf(predecessor)];
- if (opd->asConst()) {
- moveFrom = opd;
- } else {
- Temp *t = opd->asTemp();
- Q_ASSERT(t);
-
- for (const LifeTimeInterval *it2 : _liveAtEnd.at(predecessor->index())) {
- if (it2->temp() == *t
- && it2->reg() != LifeTimeInterval::InvalidRegister
- && it2->covers(predecessorEnd)) {
- moveFrom = createPhysicalRegister(it2, t->type);
- break;
- }
- }
- if (!moveFrom)
- moveFrom = createTemp(Temp::StackSlot,
- _assignedSpillSlots[t->index],
- t->type);
- }
- }
- } else {
- for (const LifeTimeInterval *predIt : _liveAtEnd.at(predecessor->index())) {
- if (predIt->temp() == it->temp()) {
- if (predIt->reg() != LifeTimeInterval::InvalidRegister
- && predIt->covers(predecessorEnd)) {
- moveFrom = createPhysicalRegister(predIt, predIt->temp().type);
- } else {
- int spillSlot = _assignedSpillSlots[predIt->temp().index];
- if (spillSlot != -1)
- moveFrom = createTemp(Temp::StackSlot, spillSlot, predIt->temp().type);
- }
- break;
- }
- }
- }
- if (!moveFrom) {
-#if !defined(QT_NO_DEBUG) && 0
- bool lifeTimeHole = false;
- if (it->ranges().first().start <= successorStart && it->ranges().last().end >= successorStart)
- lifeTimeHole = !it->covers(successorStart);
-
- Q_ASSERT(!_info->isPhiTarget(it->temp()) || it->isSplitFromInterval() || lifeTimeHole);
- if (_info->def(it->temp()) != successorStart && !it->isSplitFromInterval()) {
- const int successorEnd = successor->terminator()->id();
- const int idx = successor->in.indexOf(predecessor);
- for (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.
- for (Stmt *s : successor->statements()) {
- if (Phi *phi = s->asPhi()) {
- 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);
- } else {
- // TODO: check that the first non-phi statement does not use
- // the temp.
- break;
- }
- }
- } else {
- Q_ASSERT(use.pos < static_cast<unsigned>(successorStart) ||
- use.pos > static_cast<unsigned>(successorEnd));
- }
- }
- }
-#endif
-
- continue;
- }
-
- Temp *moveTo;
- if (it->reg() == LifeTimeInterval::InvalidRegister || !it->covers(successorStart)) {
- if (!isPhiTarget) // if it->temp() is a phi target, skip it.
- continue;
- const int spillSlot = _assignedSpillSlots[it->temp().index];
- if (spillSlot == RegisterAllocator::InvalidSpillSlot)
- continue; // it has a life-time hole here.
- moveTo = createTemp(Temp::StackSlot, spillSlot, it->temp().type);
- } else {
- moveTo = createPhysicalRegister(it, it->temp().type);
- }
-
- // add move to mapping
- mapping.add(moveFrom, moveTo);
- }
-
- if (DebugRegAlloc)
- mapping.dump();
- mapping.order();
- if (DebugRegAlloc)
- mapping.dump();
-
- bool insertIntoPredecessor = successor->in.size() > 1;
- mapping.insertMoves(insertIntoPredecessor ? predecessor : successor, _function,
- insertIntoPredecessor);
-
- if (DebugRegAlloc) {
- qDebug() << ".. done, result:";
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinterWithPositions printer(&qout, _intervals);
- printer.print(predecessor);
- printer.print(successor);
- qDebug("%s", buf.data().constData());
- }
- }
-
- Temp *createTemp(Temp::Kind kind, int index, Type type) const
- {
- Q_ASSERT(index >= 0);
- Temp *t = _function->New<Temp>();
- t->init(kind, index);
- t->type = type;
- return t;
- }
-
- Temp *createPhysicalRegister(const LifeTimeInterval *i, Type type) const
- {
- const RegisterInfo *ri = platformRegister(*i);
- Q_ASSERT(ri);
- return createTemp(Temp::PhysicalRegister, ri->reg<int>(), type);
- }
-
- const RegisterInfo *platformRegister(const LifeTimeInterval &i) const
- {
- if (i.isFP())
- return _fpRegs.value(i.reg(), 0);
- else
- return _intRegs.value(i.reg(), 0);
- }
-
- Move *generateSpill(int spillSlot, Type type, int pReg) const
- {
- Q_ASSERT(spillSlot >= 0);
-
- Move *store = _function->NewStmt<Move>();
- store->init(createTemp(Temp::StackSlot, spillSlot, type),
- createTemp(Temp::PhysicalRegister, pReg, type));
- return store;
- }
-
- Move *generateUnspill(const Temp &t, int pReg) const
- {
- Q_ASSERT(pReg >= 0);
- int spillSlot = _assignedSpillSlots[t.index];
- Q_ASSERT(spillSlot != -1);
- Move *load = _function->NewStmt<Move>();
- load->init(createTemp(Temp::PhysicalRegister, pReg, t.type),
- createTemp(Temp::StackSlot, spillSlot, t.type));
- return load;
- }
-
-private:
- void visit(Expr *e)
- {
- switch (e->exprKind) {
- case Expr::TempExpr:
- visitTemp(e->asTemp());
- break;
- default:
- EXPR_VISIT_ALL_KINDS(e);
- break;
- }
- }
-
- void visitTemp(Temp *t)
- {
- if (t->kind != Temp::VirtualRegister)
- return;
-
- const LifeTimeInterval *i = findLiveInterval(t);
- Q_ASSERT(i->isValid());
-
- if (_currentStmt != 0 && i->start() == usePosition(_currentStmt)) {
- Q_ASSERT(i->isSplitFromInterval());
- const RegisterInfo *pReg = platformRegister(*i);
- Q_ASSERT(pReg);
- _loads.push_back(generateUnspill(i->temp(), pReg->reg<int>()));
- }
-
- if (i->reg() != LifeTimeInterval::InvalidRegister &&
- (i->covers(defPosition(_currentStmt)) ||
- i->covers(usePosition(_currentStmt)))) {
- const RegisterInfo *pReg = platformRegister(*i);
- Q_ASSERT(pReg);
- t->kind = Temp::PhysicalRegister;
- t->index = pReg->reg<unsigned>();
- } else {
- int stackSlot = _assignedSpillSlots[t->index];
- Q_ASSERT(stackSlot >= 0);
- t->kind = Temp::StackSlot;
- t->index = stackSlot;
- }
- }
-
- void visit(Stmt *s)
- {
- switch (s->stmtKind) {
- case Stmt::MoveStmt: {
- auto m = s->asMove();
- if (Temp *t = m->target->asTemp())
- maybeGenerateSpill(t);
-
- visit(m->source);
- visit(m->target);
- } break;
- case Stmt::PhiStmt: {
- auto p = s->asPhi();
- maybeGenerateSpill(p->targetTemp);
- } break;
- default:
- STMT_VISIT_ALL_KINDS(s);
- break;
- }
- }
-};
-} // anonymous namespace
-
-RegisterAllocator::RegisterAllocator(const QV4::JIT::RegisterInformation &registerInformation)
- : _registerInformation(registerInformation)
-{
- for (int i = 0, ei = registerInformation.size(); i != ei; ++i) {
- const RegisterInfo &regInfo = registerInformation.at(i);
- if (regInfo.useForRegAlloc()) {
- if (regInfo.isRegularRegister())
- _normalRegisters.append(&regInfo);
- else
- _fpRegisters.append(&regInfo);
- }
- }
- Q_ASSERT(_normalRegisters.size() >= 2);
- Q_ASSERT(_fpRegisters.size() >= 2);
- _active.reserve((_normalRegisters.size() + _fpRegisters.size()) * 2);
- _inactive.reserve(_active.size());
-
- _regularRegsInUse.resize(_normalRegisters.size());
- _fpRegsInUse.resize(_fpRegisters.size());
-}
-
-RegisterAllocator::~RegisterAllocator()
-{
-}
-
-void RegisterAllocator::run(IR::Function *function, const Optimizer &opt)
-{
- _lastAssignedRegister.assign(function->tempCount, LifeTimeInterval::InvalidRegister);
- _assignedSpillSlots.assign(function->tempCount, InvalidSpillSlot);
- _activeSpillSlots.resize(function->tempCount);
-
- if (DebugRegAlloc)
- qDebug() << "*** Running regalloc for function" << (function->name ? qPrintable(*function->name) : "NO NAME") << "***";
-
- _lifeTimeIntervals = opt.lifeTimeIntervals();
-
- _unhandled = _lifeTimeIntervals->intervals();
- _handled.reserve(_unhandled.size());
-
- _info.reset(new RegAllocInfo);
- _info->collect(function, _lifeTimeIntervals);
-
- if (DebugRegAlloc) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Ranges:" << endl;
- QVector<LifeTimeInterval *> intervals = _unhandled;
- std::reverse(intervals.begin(), intervals.end());
- for (const LifeTimeInterval *r : qAsConst(intervals)) {
- r->dump(qout);
- qout << endl;
- }
- qDebug("%s", buf.data().constData());
- _info->dump();
-
- qDebug() << "*** Before register allocation:";
- buf.setData(QByteArray());
- IRPrinterWithPositions(&qout, _lifeTimeIntervals).print(function);
- qDebug("%s", buf.data().constData());
- }
- prepareRanges();
-
- linearScan();
-
- if (DebugRegAlloc)
- dump(function);
-
- // sort the ranges in reverse order, so the ResolutionPhase can take from the end (and thereby
- // prevent the copy overhead that taking from the beginning would give).
- std::sort(_handled.begin(), _handled.end(),
- [](const LifeTimeInterval *r1, const LifeTimeInterval *r2) -> bool {
- return LifeTimeInterval::lessThan(r2, r1);
- });
- ResolutionPhase(std::move(_handled), _lifeTimeIntervals, function, _assignedSpillSlots, _normalRegisters, _fpRegisters).run();
-
- function->tempCount = *std::max_element(_assignedSpillSlots.begin(), _assignedSpillSlots.end()) + 1;
-
- if (DebugRegAlloc)
- qDebug() << "*** Finished regalloc , result:";
-
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR");
- if (showCode) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinterWithRegisters(&qout, _lifeTimeIntervals, _registerInformation).print(function);
- qDebug("%s", buf.data().constData());
- }
-}
-
-RegisterInformation RegisterAllocator::usedRegisters() const
-{
- RegisterInformation regInfo;
-
- for (int i = 0, ei = _normalRegisters.size(); i != ei; ++i) {
- if (_regularRegsInUse.testBit(i))
- regInfo.append(*_normalRegisters.at(i));
- }
-
- for (int i = 0, ei = _fpRegisters.size(); i != ei; ++i) {
- if (_fpRegsInUse.testBit(i))
- regInfo.append(*_fpRegisters.at(i));
- }
-
- return regInfo;
-}
-
-void RegisterAllocator::markInUse(int reg, bool isFPReg)
-{
- if (isFPReg)
- _fpRegsInUse.setBit(reg);
- else
- _regularRegsInUse.setBit(reg);
-}
-
-static inline LifeTimeInterval createFixedInterval(int rangeCount)
-{
- LifeTimeInterval i(rangeCount);
- i.setReg(0);
-
- Temp t;
- t.init(Temp::PhysicalRegister, 0);
- t.type = IR::SInt32Type;
- i.setTemp(t);
-
- return i;
-}
-
-LifeTimeInterval *RegisterAllocator::cloneFixedInterval(int reg, bool isFP, const LifeTimeInterval &original)
-{
- LifeTimeInterval *lti = new LifeTimeInterval(original);
- _lifeTimeIntervals->add(lti);
- lti->setReg(reg);
- lti->setFixedInterval(true);
-
- Temp t;
- t.init(Temp::PhysicalRegister, reg);
- t.type = isFP ? IR::DoubleType : IR::SInt32Type;
- lti->setTemp(t);
-
- return lti;
-}
-
-// Creates the intervals with fixed ranges. See [Wimmer2]. Note that this only applies to callee-
-// saved registers.
-void RegisterAllocator::prepareRanges()
-{
- LifeTimeInterval ltiWithCalls = createFixedInterval(int(_info->calls().size()));
- for (int callPosition : _info->calls())
- ltiWithCalls.addRange(callPosition, callPosition);
-
- const int regCount = _normalRegisters.size();
- _fixedRegisterRanges.resize(regCount);
- for (int reg = 0; reg < regCount; ++reg) {
- if (_normalRegisters.at(reg)->isCallerSaved()) {
- LifeTimeInterval *lti = cloneFixedInterval(reg, false, ltiWithCalls);
- if (lti->isValid()) {
- _fixedRegisterRanges[reg] = lti;
- _active.append(lti);
- }
- }
- }
-
- const int fpRegCount = _fpRegisters.size();
- _fixedFPRegisterRanges.resize(fpRegCount);
- for (int fpReg = 0; fpReg < fpRegCount; ++fpReg) {
- if (_fpRegisters.at(fpReg)->isCallerSaved()) {
- LifeTimeInterval *lti = cloneFixedInterval(fpReg, true, ltiWithCalls);
- if (lti->isValid()) {
- _fixedFPRegisterRanges[fpReg] = lti;
- _active.append(lti);
- }
- }
- }
-}
-
-void RegisterAllocator::linearScan()
-{
- while (!_unhandled.isEmpty()) {
- LifeTimeInterval *current = _unhandled.back();
- _unhandled.pop_back();
- const int position = current->start();
-
- // check for intervals in active that are handled or inactive
- for (int i = 0; i < _active.size(); ) {
- LifeTimeInterval *it = _active.at(i);
- if (it->end() < position) {
- if (!it->isFixedInterval())
- _handled += it;
- _active.remove(i);
- } else if (!it->covers(position)) {
- _inactive += it;
- _active.remove(i);
- } else {
- ++i;
- }
- }
-
- // check for intervals in inactive that are handled or active
- for (int i = 0; i < _inactive.size(); ) {
- LifeTimeInterval *it = _inactive.at(i);
- if (it->end() < position) {
- if (!it->isFixedInterval())
- _handled += it;
- _inactive.remove(i);
- } else if (it->covers(position)) {
- if (it->reg() != LifeTimeInterval::InvalidRegister) {
- _active += it;
- _inactive.remove(i);
- } else {
- // although this interval is now active, it has no register allocated (always
- // spilled), so leave it in inactive.
- ++i;
- }
- } else {
- ++i;
- }
- }
-
- Q_ASSERT(!current->isFixedInterval());
-
-#ifdef DEBUG_REGALLOC
- qDebug() << "** Position" << position;
-#endif // DEBUG_REGALLOC
-
- if (_info->canHaveRegister(current->temp())) {
- tryAllocateFreeReg(*current);
- if (current->reg() == LifeTimeInterval::InvalidRegister)
- allocateBlockedReg(*current);
- if (current->reg() != LifeTimeInterval::InvalidRegister)
- _active += current;
- } else {
- assignSpillSlot(current->temp(), current->start(), current->end());
- _inactive += current;
- if (DebugRegAlloc)
- qDebug() << "*** allocating stack slot" << _assignedSpillSlots[current->temp().index]
- << "for %" << current->temp().index << "as it cannot be loaded in a register";
- }
- }
-
- for (LifeTimeInterval *r : qAsConst(_active))
- if (!r->isFixedInterval())
- _handled.append(r);
- _active.clear();
- for (LifeTimeInterval *r : qAsConst(_inactive))
- if (!r->isFixedInterval())
- _handled.append(r);
- _inactive.clear();
-}
-
-static inline int indexOfRangeCoveringPosition(const LifeTimeInterval::Ranges &ranges, int position)
-{
- for (int i = 0, ei = ranges.size(); i != ei; ++i) {
- if (position <= ranges[i].end)
- return i;
- }
- return -1;
-}
-
-static inline int intersectionPosition(const LifeTimeIntervalRange &one, const LifeTimeIntervalRange &two)
-{
- if (one.covers(two.start))
- return two.start;
- if (two.covers(one.start))
- return one.start;
- return -1;
-}
-
-static inline bool isFP(const Temp &t)
-{ return t.type == DoubleType; }
-
-static inline bool candidateIsBetterFit(int bestSizeSoFar, int idealSize, int candidateSize)
-{
- // If the candidateSize is larger than the current we take it only if the current size does not
- // yet fit for the whole interval.
- if (bestSizeSoFar < candidateSize && bestSizeSoFar < idealSize)
- return true;
-
- // If the candidateSize is smaller we only take it if it still fits the whole interval.
- if (bestSizeSoFar > candidateSize && candidateSize >= idealSize)
- return true;
-
- // Other wise: no luck.
- return false;
-}
-
-// Out of all available registers (with their next-uses), choose the one that fits the requested
-// duration best. This can return a register that is not free for the whole interval, but that's
-// fine: we just have to split the current interval.
-static void longestAvailableReg(int *nextUses, int nextUseCount, int &reg, int &freeUntilPos_reg, int lastUse)
-{
- reg = LifeTimeInterval::InvalidRegister;
- freeUntilPos_reg = 0;
-
- for (int candidate = 0, candidateEnd = nextUseCount; candidate != candidateEnd; ++candidate) {
- int fp = nextUses[candidate];
- if (candidateIsBetterFit(freeUntilPos_reg, lastUse, fp)) {
- reg = candidate;
- freeUntilPos_reg = fp;
- }
- }
-}
-
-#define CALLOC_ON_STACK(ty, ptr, sz, val) \
- Q_ASSERT(sz > 0); \
- Q_ALLOCA_VAR(ty, ptr, sizeof(ty) * (sz)); \
- for (ty *it = ptr, *eit = ptr + (sz); it != eit; ++it) \
- *it = val;
-
-// Try to allocate a register that's currently free.
-void RegisterAllocator::tryAllocateFreeReg(LifeTimeInterval &current)
-{
- Q_ASSERT(!current.isFixedInterval());
- Q_ASSERT(current.reg() == LifeTimeInterval::InvalidRegister);
-
- const bool needsFPReg = isFP(current.temp());
- const int freeUntilPosCount = needsFPReg ? _fpRegisters.size() : _normalRegisters.size();
- CALLOC_ON_STACK(int, freeUntilPos, freeUntilPosCount, INT_MAX);
-
- for (Intervals::const_iterator i = _active.constBegin(), ei = _active.constEnd(); i != ei; ++i) {
- const LifeTimeInterval *it = *i;
- if (it->isFP() == needsFPReg)
- freeUntilPos[it->reg()] = 0; // mark register as unavailable
- }
-
- for (Intervals::const_iterator i = _inactive.constBegin(), ei = _inactive.constEnd(); i != ei; ++i) {
- const LifeTimeInterval *it = *i;
- if (it->isFP() != needsFPReg)
- continue; // different register type, so not applicable.
- if (it->reg() == LifeTimeInterval::InvalidRegister)
- continue; // this range does not block a register from being used, as it has no register assigned
-
- if (current.isSplitFromInterval() || it->isFixedInterval()) {
- const int intersectionPos = nextIntersection(current, *it);
- if (intersectionPos != -1)
- freeUntilPos[it->reg()] = qMin(freeUntilPos[it->reg()], intersectionPos);
- }
- }
-
- int reg = LifeTimeInterval::InvalidRegister;
- int freeUntilPos_reg = 0;
-
- const RegAllocInfo::Hints &hints = _info->hints(current.temp());
- for (RegAllocInfo::Hints::const_iterator i = hints.begin(), ei = hints.end(); i != ei; ++i) {
- const Temp &hint = *i;
- int candidate;
- if (hint.kind == Temp::PhysicalRegister)
- candidate = hint.index;
- else
- candidate = _lastAssignedRegister[hint.index];
-
- const int end = current.end();
- if (candidate == LifeTimeInterval::InvalidRegister)
- continue; // the candidate has no register assigned, so it cannot be (re-)used
- if (current.isFP() != isFP(hint))
- continue; // different register type, so not applicable.
-
- const int fp = freeUntilPos[candidate];
- if (candidateIsBetterFit(freeUntilPos_reg, end, fp)) {
- reg = candidate;
- freeUntilPos_reg = fp;
- }
- }
-
- // None of the hinted registers could fit the interval, so try all registers next.
- if (reg == LifeTimeInterval::InvalidRegister)
- longestAvailableReg(freeUntilPos, freeUntilPosCount, reg, freeUntilPos_reg, current.end());
-
- if (freeUntilPos_reg == 0) {
- // no register available without spilling
- if (DebugRegAlloc)
- qDebug("*** no register available for %u", current.temp().index);
- return;
- } else if (current.end() < freeUntilPos_reg) {
- // register available for the whole interval
- if (DebugRegAlloc)
- qDebug() << "*** allocating register" << reg << "for the whole interval of %" << current.temp().index;
- current.setReg(reg);
- _lastAssignedRegister[current.temp().index] = reg;
- markInUse(reg, needsFPReg);
- } else {
- // register available for the first part of the interval
-
- // TODO: this is slightly inefficient in the following case:
- // %1 = something
- // some_call(%1)
- // %2 = %1 + 1
- // Now %1 will get a register assigned, and will be spilled to the stack immediately. It
- // would be better to check if there are actually uses in the range before the split.
-
- current.setReg(reg);
- _lastAssignedRegister[current.temp().index] = reg;
- if (DebugRegAlloc)
- qDebug() << "*** allocating register" << reg << "for the first part of interval of %" << current.temp().index;
- split(current, freeUntilPos_reg, true);
- markInUse(reg, needsFPReg);
- }
-}
-
-// This gets called when all registers are currently in use.
-void RegisterAllocator::allocateBlockedReg(LifeTimeInterval &current)
-{
- Q_ASSERT(!current.isFixedInterval());
- Q_ASSERT(current.reg() == LifeTimeInterval::InvalidRegister);
- const int position = current.start();
-
- const bool isPhiTarget = _info->isPhiTarget(current.temp());
- if (isPhiTarget && !current.isSplitFromInterval()) {
- // Special case: storing to a phi-node's target will result in a single move. So, if we
- // would spill another interval to the stack (that's 1 store), and then do the move for the
- // phi target (at least 1 move or a load), that would result in 2 instructions. Instead, we
- // force the phi-node's target to go to the stack immediately, which is always a single
- // store.
- split(current, position + 1, true);
- _inactive.append(&current);
- return;
- }
-
- const bool needsFPReg = isFP(current.temp());
- const int nextUsePosCount = needsFPReg ? _fpRegisters.size() : _normalRegisters.size();
- CALLOC_ON_STACK(int, nextUsePos, nextUsePosCount, INT_MAX);
- QVector<LifeTimeInterval *> nextUseRangeForReg(nextUsePosCount, 0);
-
- for (Intervals::const_iterator i = _active.constBegin(), ei = _active.constEnd(); i != ei; ++i) {
- LifeTimeInterval &it = **i;
- if (it.isFP() != needsFPReg)
- continue; // different register type, so not applicable.
-
- const int nu = it.isFixedInterval() ? 0 : nextUse(it.temp(), current.start());
- if (nu == position) {
- nextUsePos[it.reg()] = 0;
- } else if (nu != -1 && nu < nextUsePos[it.reg()]) {
- nextUsePos[it.reg()] = nu;
- nextUseRangeForReg[it.reg()] = &it;
- } else if (nu == -1 && nextUsePos[it.reg()] == INT_MAX) {
- // in a loop, the range can be active, but the result might only be used before the
- // current position (e.g. the induction variable being used in the phi node in the loop
- // header). So, we can use this register, but we need to remember to split the interval
- // in order to have the edge-resolving generate a load at the edge going back to the
- // loop header.
- nextUseRangeForReg[it.reg()] = &it;
- }
- }
-
- for (Intervals::const_iterator i = _inactive.constBegin(), ei = _inactive.constEnd(); i != ei; ++i) {
- LifeTimeInterval &it = **i;
- if (it.isFP() != needsFPReg)
- continue; // different register type, so not applicable.
- if (it.reg() == LifeTimeInterval::InvalidRegister)
- continue; // this range does not block a register from being used, as it has no register assigned
-
- if (current.isSplitFromInterval() || it.isFixedInterval()) {
- if (nextIntersection(current, it) != -1) {
- const int nu = nextUse(it.temp(), current.start());
- if (nu != -1 && nu < nextUsePos[it.reg()]) {
- nextUsePos[it.reg()] = nu;
- nextUseRangeForReg[it.reg()] = &it;
- }
- }
- }
- }
-
- int reg, nextUsePos_reg;
- longestAvailableReg(nextUsePos, nextUsePosCount, reg, nextUsePos_reg, current.end());
-
- Q_ASSERT(current.start() <= nextUsePos_reg);
-
- // spill interval that currently block reg
- if (DebugRegAlloc) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream out(&buf);
- out << "*** spilling intervals that block reg " <<reg<< " for interval ";
- current.dump(out);
- qDebug("%s", buf.data().constData());
- }
- current.setReg(reg);
- _lastAssignedRegister[current.temp().index] = reg;
- LifeTimeInterval *nextUse = nextUseRangeForReg[reg];
- Q_ASSERT(nextUse);
- Q_ASSERT(!nextUse->isFixedInterval());
-
- split(*nextUse, position, /*skipOptionalRegisterUses =*/ true);
-
- // We might have chosen a register that is used by a range that has a hole in its life time.
- // If that's the case, check if the current interval completely fits in the hole. Or rephrased:
- // check if the current interval will use the register after that hole ends (so that range, and
- // if so, split that interval so that it gets a new register assigned when it needs one.
- splitInactiveAtEndOfLifetimeHole(reg, needsFPReg, position);
-
- // make sure that current does not intersect with the fixed interval for reg
- const LifeTimeInterval *fixedRegRange = needsFPReg ? _fixedFPRegisterRanges.at(reg)
- : _fixedRegisterRanges.at(reg);
- if (fixedRegRange) {
- int ni = nextIntersection(current, *fixedRegRange);
- if (ni != -1) {
- if (DebugRegAlloc) {
- qDebug("***-- current range intersects with a fixed reg use at %d, so splitting it.", ni);
- }
- // current does overlap with a fixed interval, so split current before that intersection.
- split(current, ni, true);
- }
- }
-}
-
-int RegisterAllocator::nextIntersection(const LifeTimeInterval &current,
- const LifeTimeInterval &another) const
-{
- const LifeTimeInterval::Ranges &currentRanges = current.ranges();
- int currentIt = 0;
-
- const LifeTimeInterval::Ranges &anotherRanges = another.ranges();
- const int anotherItStart = indexOfRangeCoveringPosition(anotherRanges, current.start());
- if (anotherItStart == -1)
- return -1;
-
- for (int currentEnd = currentRanges.size(); currentIt < currentEnd; ++currentIt) {
- const LifeTimeIntervalRange currentRange = currentRanges.at(currentIt);
- for (int anotherIt = anotherItStart, anotherEnd = anotherRanges.size(); anotherIt < anotherEnd; ++anotherIt) {
- const LifeTimeIntervalRange anotherRange = anotherRanges.at(anotherIt);
- if (anotherRange.start > currentRange.end)
- break;
- int intersectPos = intersectionPosition(currentRange, anotherRange);
- if (intersectPos != -1)
- return intersectPos;
- }
- }
-
- return -1;
-}
-
-/// Find the first use after the start position for the given temp.
-///
-/// This is only called when all registers are in use, and when one of them has to be spilled to the
-/// stack. So, uses where a register is optional can be ignored.
-int RegisterAllocator::nextUse(const Temp &t, int startPosition) const
-{
- typedef std::vector<Use>::const_iterator ConstIt;
-
- const std::vector<Use> &usePositions = _info->uses(t);
- const ConstIt cend = usePositions.end();
- for (ConstIt it = usePositions.begin(); it != cend; ++it) {
- if (it->mustHaveRegister()) {
- const int usePos = it->pos;
- if (usePos >= startPosition)
- return usePos;
- }
- }
-
- return -1;
-}
-
-static inline void insertReverseSorted(QVector<LifeTimeInterval *> &intervals, LifeTimeInterval *newInterval)
-{
- newInterval->validate();
- for (int i = intervals.size(); i > 0;) {
- if (LifeTimeInterval::lessThan(newInterval, intervals.at(--i))) {
- intervals.insert(i + 1, newInterval);
- return;
- }
- }
- intervals.insert(0, newInterval);
-}
-
-void RegisterAllocator::split(LifeTimeInterval &current, int beforePosition,
- bool skipOptionalRegisterUses)
-{ // TODO: check if we can always skip the optional register uses
- Q_ASSERT(!current.isFixedInterval());
-
- if (DebugRegAlloc) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream out(&buf);
- out << "***** split request for range ";
- current.dump(out);
- out << " before position " << beforePosition
- << " and skipOptionalRegisterUses = " << skipOptionalRegisterUses << endl;
- qDebug("%s", buf.data().constData());
- }
-
- assignSpillSlot(current.temp(), current.start(), current.end());
-
- const int firstPosition = current.start();
- Q_ASSERT(beforePosition > firstPosition && "split before start");
-
- int lastUse = firstPosition;
- int nextUse = -1;
- const std::vector<Use> &usePositions = _info->uses(current.temp());
- for (size_t i = 0, ei = usePositions.size(); i != ei; ++i) {
- const Use &usePosition = usePositions.at(i);
- const int usePos = usePosition.pos;
- if (lastUse < usePos && usePos < beforePosition) {
- lastUse = usePos;
- } else if (usePos >= beforePosition) {
- if (!skipOptionalRegisterUses || usePosition.mustHaveRegister()) {
- nextUse = usePos;
- break;
- }
- }
- }
- Q_ASSERT(lastUse != -1);
- Q_ASSERT(lastUse < beforePosition);
-
- LifeTimeInterval newInterval = current.split(lastUse, nextUse);
- if (DebugRegAlloc) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream out(&buf);
- out << "***** last use = " << lastUse << ", nextUse = " << nextUse << endl;
- out << "***** new interval: ";
- newInterval.dump(out);
- out << endl;
- out << "***** preceding interval: ";
- current.dump(out);
- out << endl;
- qDebug("%s", buf.data().constData());
- }
- if (newInterval.isValid()) {
- if (current.reg() != LifeTimeInterval::InvalidRegister)
- _info->addHint(current.temp(), current.reg());
- newInterval.setReg(LifeTimeInterval::InvalidRegister);
- LifeTimeInterval *newIntervalPtr = new LifeTimeInterval(newInterval);
- _lifeTimeIntervals->add(newIntervalPtr);
- insertReverseSorted(_unhandled, newIntervalPtr);
- }
-}
-
-void RegisterAllocator::splitInactiveAtEndOfLifetimeHole(int reg, bool isFPReg, int position)
-{
- for (int i = 0, ei = _inactive.size(); i != ei; ++i) {
- LifeTimeInterval &interval = *_inactive[i];
- if (interval.isFixedInterval())
- continue;
- if (isFPReg == interval.isFP() && interval.reg() == reg) {
- LifeTimeInterval::Ranges ranges = interval.ranges();
- int endOfLifetimeHole = -1;
- for (int j = 0, ej = ranges.size(); j != ej; ++j) {
- if (position < ranges[j].start)
- endOfLifetimeHole = ranges[j].start;
- }
- if (endOfLifetimeHole != -1)
- split(interval, endOfLifetimeHole);
- }
- }
-}
-
-void RegisterAllocator::assignSpillSlot(const Temp &t, int startPos, int endPos)
-{
- if (_assignedSpillSlots[t.index] != InvalidSpillSlot)
- return;
-
- for (int i = 0, ei = _activeSpillSlots.size(); i != ei; ++i) {
- if (_activeSpillSlots.at(i) < startPos) {
- _activeSpillSlots[i] = endPos;
- _assignedSpillSlots[t.index] = i;
- return;
- }
- }
-
- Q_UNREACHABLE();
-}
-
-void RegisterAllocator::dump(IR::Function *function) const
-{
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinterWithPositions printer(&qout, _lifeTimeIntervals);
-
- qout << "Ranges:" << endl;
- QVector<LifeTimeInterval *> handled = _handled;
- std::sort(handled.begin(), handled.end(), LifeTimeInterval::lessThanForTemp);
- for (const LifeTimeInterval *r : qAsConst(handled)) {
- r->dump(qout);
- qout << endl;
- }
-
- qout << "Spill slots:" << endl;
- for (unsigned i = 0; i < _assignedSpillSlots.size(); ++i)
- if (_assignedSpillSlots[i] != InvalidSpillSlot)
- qout << "\t%" << i << " -> " << _assignedSpillSlots[i] << endl;
-
- printer.print(function);
- qDebug("%s", buf.data().constData());
-}
-
-// References:
-// [Wimmer1] C. Wimmer and M. Franz. Linear Scan Register Allocation on SSA Form. In Proceedings of
-// 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.
-// [Traub] Omri Traub, Glenn Holloway, and Michael D. Smith. Quality and Speed in Linear-scan
-// Register Allocation. In Proceedings of the ACM SIGPLAN 1998 Conference on Programming
-// Language Design and Implementation, pages 142-151, June 1998.