V4 JIT: parameterize the prologue and epilogue generation
authorErik Verbruggen <erik.verbruggen@digia.com>
Tue, 12 Aug 2014 07:40:11 +0000 (09:40 +0200)
committerErik Verbruggen <erik.verbruggen@digia.com>
Thu, 14 Aug 2014 13:27:51 +0000 (15:27 +0200)
... with the regular (non-FP) registers that need to be saved. This
patch shouldn't change any of the JIT generated code, because all
regular callee saved registers are passed in.

Change-Id: Id11b8f37f06d80e8015ac6f0d0ccefdfa3342cbe
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
src/qml/jit/qv4assembler.cpp
src/qml/jit/qv4assembler_p.h
src/qml/jit/qv4isel_masm.cpp
src/qml/jit/qv4isel_masm_p.h
src/qml/jit/qv4registerinfo_p.h
src/qml/jit/qv4targetplatform_p.h

index 3f3fccb..e76d498 100644 (file)
@@ -93,10 +93,8 @@ QV4::ExecutableAllocator::ChunkOfPages *CompilationUnit::chunkForFunction(int fu
 
 const Assembler::VoidType Assembler::Void;
 
-Assembler::Assembler(InstructionSelection *isel, IR::Function* function, QV4::ExecutableAllocator *executableAllocator,
-                     int maxArgCountForBuiltins)
-    : _stackLayout(function, maxArgCountForBuiltins)
-    , _constTable(this)
+Assembler::Assembler(InstructionSelection *isel, IR::Function* function, QV4::ExecutableAllocator *executableAllocator)
+    : _constTable(this)
     , _function(function)
     , _nextBlock(0)
     , _executableAllocator(executableAllocator)
@@ -241,7 +239,7 @@ void Assembler::storeValue(QV4::Primitive value, IR::Expr *destination)
     storeValue(value, addr);
 }
 
-void Assembler::enterStandardStackFrame()
+void Assembler::enterStandardStackFrame(const RegisterInformation &regularRegistersToSave)
 {
     platformEnterStandardStackFrame(this);
 
@@ -250,24 +248,28 @@ void Assembler::enterStandardStackFrame()
     push(StackFrameRegister);
     move(StackPointerRegister, StackFrameRegister);
 
-    int frameSize = _stackLayout.calculateStackFrameSize();
+    int frameSize = _stackLayout->calculateStackFrameSize();
 
     subPtr(TrustedImm32(frameSize), StackPointerRegister);
 
-    const RegisterInformation &calleeSavedRegisters = getCalleeSavedRegisters();
-    for (int i = 0; i < calleeSavedRegisterCount(); ++i)
-        storePtr(calleeSavedRegisters[i].reg<RegisterID>(), Address(StackFrameRegister, -(i + 1) * sizeof(void*)));
-
+    Address slotAddr(StackFrameRegister, -RegisterSize);
+    for (int i = 0, ei = regularRegistersToSave.size(); i < ei; ++i) {
+        storePtr(regularRegistersToSave.at(i).reg<RegisterID>(), slotAddr);
+        slotAddr.offset -= RegisterSize;
+    }
 }
 
-void Assembler::leaveStandardStackFrame()
+void Assembler::leaveStandardStackFrame(const RegisterInformation &regularRegistersToSave)
 {
+    Address slotAddr(StackFrameRegister, -regularRegistersToSave.size() * RegisterSize);
+
     // restore the callee saved registers
-    const RegisterInformation &calleeSavedRegisters = getCalleeSavedRegisters();
-    for (int i = calleeSavedRegisterCount() - 1; i >= 0; --i)
-        loadPtr(Address(StackFrameRegister, -(i + 1) * sizeof(void*)), calleeSavedRegisters[i].reg<RegisterID>());
+    for (int i = regularRegistersToSave.size() - 1; i >= 0; --i) {
+        loadPtr(slotAddr, regularRegistersToSave.at(i).reg<RegisterID>());
+        slotAddr.offset += RegisterSize;
+    }
 
-    int frameSize = _stackLayout.calculateStackFrameSize();
+    int frameSize = _stackLayout->calculateStackFrameSize();
     // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't
     // work well for large immediates.
 #if CPU(ARM_THUMB2)
@@ -388,4 +390,9 @@ Assembler::Jump Assembler::branchInt32(bool invertCondition, IR::AluOp op, IR::E
                                          toInt32Register(right, Assembler::ReturnValueRegister));
 }
 
+void Assembler::setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave)
+{
+    _stackLayout.reset(new StackLayout(_function, maxArgCountForBuiltins, regularRegistersToSave));
+}
+
 #endif
index 5e868c3..7d774f9 100644 (file)
@@ -135,9 +135,10 @@ struct ExceptionCheck<void (*)(QV4::NoThrowContext *, A, B, C)> {
 
 class Assembler : public JSC::MacroAssembler, public TargetPlatform
 {
+    Q_DISABLE_COPY(Assembler)
+
 public:
-    Assembler(InstructionSelection *isel, IR::Function* function, QV4::ExecutableAllocator *executableAllocator,
-              int maxArgCountForBuiltins);
+    Assembler(InstructionSelection *isel, IR::Function* function, QV4::ExecutableAllocator *executableAllocator);
 
     // Explicit type to allow distinguishing between
     // pushing an address itself or the value it points
@@ -181,8 +182,8 @@ public:
     class StackLayout
     {
     public:
-        StackLayout(IR::Function *function, int maxArgCountForBuiltins)
-            : calleeSavedRegCount(Assembler::calleeSavedRegisterCount() + 1)
+        StackLayout(IR::Function *function, int maxArgCountForBuiltins, int normalRegistersToSave)
+            : normalRegistersToSave(normalRegistersToSave)
             , maxOutgoingArgumentCount(function->maxNumberOfArguments)
             , localCount(function->tempCount)
             , savedRegCount(maxArgCountForBuiltins)
@@ -215,7 +216,7 @@ public:
                                                      + RegisterSize; // saved StackFrameRegister
 
             // space for the callee saved registers
-            int frameSize = RegisterSize * calleeSavedRegisterCount();
+            int frameSize = RegisterSize * normalRegistersToSave;
             frameSize += savedRegCount * sizeof(QV4::Value); // these get written out as Values, not as native registers
 
             Q_ASSERT(frameSize + stackSpaceAllocatedOtherwise < INT_MAX);
@@ -268,11 +269,11 @@ public:
         int calleeSavedRegisterSpace() const
         {
             // plus 1 for the old FP
-            return RegisterSize * (calleeSavedRegCount + 1);
+            return RegisterSize * (normalRegistersToSave + 1);
         }
 
     private:
-        int calleeSavedRegCount;
+        int normalRegistersToSave;
 
         /// arg count for calls to JS functions
         int maxOutgoingArgumentCount;
@@ -384,7 +385,7 @@ public:
     {
         Q_ASSERT(t->kind == IR::Temp::StackSlot);
 
-        return Pointer(_stackLayout.stackSlotPointer(t->index));
+        return Pointer(_stackLayout->stackSlotPointer(t->index));
     }
 
     template <int argumentNumber>
@@ -394,7 +395,7 @@ public:
             return;
         if (IR::Temp *t = arg.value->asTemp()) {
             if (t->kind == IR::Temp::PhysicalRegister) {
-                Pointer addr(_stackLayout.savedRegPointer(argumentNumber));
+                Pointer addr(_stackLayout->savedRegPointer(argumentNumber));
                 switch (t->type) {
                 case IR::BoolType:
                     storeBool((RegisterID) t->index, addr);
@@ -793,8 +794,8 @@ public:
 
     void storeValue(QV4::Primitive value, IR::Expr* temp);
 
-    void enterStandardStackFrame();
-    void leaveStandardStackFrame();
+    void enterStandardStackFrame(const RegisterInformation &regularRegistersToSave);
+    void leaveStandardStackFrame(const RegisterInformation &regularRegistersToSave);
 
     void checkException() {
         loadPtr(Address(ContextRegister, qOffsetOf(QV4::ExecutionContext::Data, engine)), ScratchRegister);
@@ -941,7 +942,7 @@ public:
     Pointer toAddress(RegisterID tmpReg, IR::Expr *e, int offset)
     {
         if (IR::Const *c = e->asConst()) {
-            Address addr = _stackLayout.savedRegPointer(offset);
+            Address addr = _stackLayout->savedRegPointer(offset);
             Address tagAddr = addr;
             tagAddr.offset += 4;
 
@@ -953,7 +954,7 @@ public:
 
         if (IR::Temp *t = e->asTemp())
             if (t->kind == IR::Temp::PhysicalRegister)
-                return Pointer(_stackLayout.savedRegPointer(offset));
+                return Pointer(_stackLayout->savedRegPointer(offset));
 
         return loadAddress(tmpReg, e);
     }
@@ -1145,14 +1146,15 @@ public:
 
     JSC::MacroAssemblerCodeRef link(int *codeSize);
 
-    const StackLayout stackLayout() const { return _stackLayout; }
+    void setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave);
+    const StackLayout &stackLayout() const { return *_stackLayout.data(); }
     ConstantTable &constantTable() { return _constTable; }
 
     Label exceptionReturnLabel;
     IR::BasicBlock * catchBlock;
     QVector<Jump> exceptionPropagationJumps;
 private:
-    const StackLayout _stackLayout;
+    QScopedPointer<const StackLayout> _stackLayout;
     ConstantTable _constTable;
     IR::Function *_function;
     QHash<IR::BasicBlock *, Label> _addrs;
index f5d112c..5547190 100644 (file)
@@ -226,20 +226,23 @@ void InstructionSelection::run(int functionIndex)
     static const bool withRegisterAllocator = qgetenv("QV4_NO_REGALLOC").isEmpty();
     if (Assembler::RegAllocIsSupported && opt.isInSSA() && withRegisterAllocator) {
         RegisterAllocator(Assembler::getRegisterInfo()).run(_function, opt);
+        calculateRegistersToSave(Assembler::getRegisterInfo());
     } else {
         if (opt.isInSSA())
             // No register allocator available for this platform, or env. var was set, so:
             opt.convertOutOfSSA();
         ConvertTemps().toStackSlots(_function);
         IR::Optimizer::showMeTheCode(_function);
+        calculateRegistersToSave(Assembler::getRegisterInfo()); // FIXME: this saves all registers. We can probably do with a subset: those that are not used by the register allocator.
     }
     QSet<IR::Jump *> removableJumps = opt.calculateOptionalJumps();
     qSwap(_removableJumps, removableJumps);
 
     Assembler* oldAssembler = _as;
-    _as = new Assembler(this, _function, executableAllocator, 6); // 6 == max argc for calls to built-ins with an argument array
-
-    _as->enterStandardStackFrame();
+    _as = new Assembler(this, _function, executableAllocator);
+    _as->setStackLayout(6, // 6 == max argc for calls to built-ins with an argument array
+                        regularRegistersToSave.size());
+    _as->enterStandardStackFrame(regularRegistersToSave);
 
 #ifdef ARGUMENTS_IN_REGISTERS
     _as->move(_as->registerForArgument(0), Assembler::ContextRegister);
@@ -1470,7 +1473,7 @@ void InstructionSelection::visitRet(IR::Ret *s)
     _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext::Data, engine)), Assembler::ScratchRegister);
     _as->storePtr(Assembler::LocalsRegister, Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop)));
 
-    _as->leaveStandardStackFrame();
+    _as->leaveStandardStackFrame(regularRegistersToSave);
     _as->ret();
 }
 
@@ -1525,6 +1528,26 @@ int InstructionSelection::prepareCallData(IR::ExprList* args, IR::Expr *thisObje
     return argc;
 }
 
+void InstructionSelection::calculateRegistersToSave(const RegisterInformation &used)
+{
+    regularRegistersToSave.clear();
+
+    foreach (const RegisterInfo &ri, Assembler::getRegisterInfo()) {
+        if (ri.isCallerSaved())
+            continue;
+
+        if (ri.isRegularRegister()) {
+#if defined(RESTORE_EBX_ON_CALL)
+            if (ri.isRegularRegister() && ri.reg<JSC::X86Registers::RegisterID>() == JSC::X86Registers::ebx) {
+                regularRegistersToSave.append(ri);
+                continue;
+            }
+#endif // RESTORE_EBX_ON_CALL
+            if (ri.isPredefined() || used.contains(ri))
+                regularRegistersToSave.append(ri);
+        }
+    }
+}
 
 QT_BEGIN_NAMESPACE
 namespace QV4 {
index af518a0..bbe0f05 100644 (file)
@@ -237,6 +237,8 @@ private:
     int prepareVariableArguments(IR::ExprList* args);
     int prepareCallData(IR::ExprList* args, IR::Expr *thisObject);
 
+    void calculateRegistersToSave(const RegisterInformation &used);
+
     template <typename Retval, typename Arg1, typename Arg2, typename Arg3>
     void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2, Arg3 arg3)
     {
@@ -263,6 +265,7 @@ private:
 
     QScopedPointer<CompilationUnit> compilationUnit;
     QQmlEnginePrivate *qmlEngine;
+    RegisterInformation regularRegistersToSave;
 };
 
 class Q_QML_EXPORT ISelFactory: public EvalISelFactory
index b8701d7..ed816fd 100644 (file)
@@ -52,27 +52,30 @@ namespace JIT {
 class RegisterInfo
 {
 public:
-    enum { InvalidRegister = -1 };
+    enum { InvalidRegister = (1 << 29) - 1 };
     enum SavedBy { CallerSaved, CalleeSaved };
     enum RegisterType { RegularRegister, FloatingPointRegister };
     enum Usage { Predefined, RegAlloc };
 
 public:
     RegisterInfo()
-        : _reg(InvalidRegister)
-        , _type(RegularRegister)
-        , _savedBy(CallerSaved)
+        : _savedBy(CallerSaved)
         , _usage(Predefined)
+        , _type(RegularRegister)
+        , _reg(InvalidRegister)
     {}
 
     RegisterInfo(int reg, const QString &prettyName, RegisterType type, SavedBy savedBy, Usage usage)
-        : _reg(reg)
-        , _prettyName(prettyName)
-        , _type(type)
+        : _prettyName(prettyName)
         , _savedBy(savedBy)
         , _usage(usage)
+        , _type(type)
+        , _reg(reg)
     {}
 
+    bool operator==(const RegisterInfo &other) const
+    { return _type == other._type && _reg == other._reg; }
+
     bool isValid() const { return _reg != InvalidRegister; }
     template <typename T> T reg() const { return static_cast<T>(_reg); }
     QString prettyName() const { return _prettyName; }
@@ -81,13 +84,14 @@ public:
     bool isFloatingPoint() const { return _type == FloatingPointRegister; }
     bool isRegularRegister() const { return _type == RegularRegister; }
     bool useForRegAlloc() const { return _usage == RegAlloc; }
+    bool isPredefined() const { return _usage == Predefined; }
 
 private:
-    int _reg;
     QString _prettyName;
-    RegisterType _type;
-    SavedBy _savedBy;
-    Usage _usage;
+    unsigned _savedBy : 1;
+    unsigned _usage   : 1;
+    unsigned _type    : 1;
+    unsigned _reg     : 29;
 };
 typedef QVector<RegisterInfo> RegisterInformation;
 
index 4b384d2..b32fe66 100644 (file)
@@ -354,30 +354,6 @@ public: // utility functions
 
         return info;
     }
-
-    static RegisterInformation &getCalleeSavedRegisters()
-    {
-        static RegisterInformation regs;
-        if (regs.isEmpty()) {
-            foreach (const RegisterInfo &info, getRegisterInfo()) {
-#if defined(RESTORE_EBX_ON_CALL)
-                if (info.reg<JSC::X86Registers::RegisterID>() == JSC::X86Registers::ebx) {
-                    regs.append(info);
-                    continue;
-                }
-#endif // RESTORE_EBX_ON_CALL
-                if (info.isCalleeSaved())
-                    regs.append(info);
-            }
-        }
-
-        return regs;
-    }
-
-    static int calleeSavedRegisterCount()
-    {
-        return getCalleeSavedRegisters().size();
-    }
 };
 
 } // JIT namespace