Index: /trunk/include/VBox/vmm/iem.h
===================================================================
--- /trunk/include/VBox/vmm/iem.h	(revision 105697)
+++ /trunk/include/VBox/vmm/iem.h	(revision 105698)
@@ -159,5 +159,5 @@
 VMMDECL(VBOXSTRICTRC)       IEMExecOneIgnoreLock(PVMCPUCC pVCpu);
 VMMDECL(VBOXSTRICTRC)       IEMExecLots(PVMCPUCC pVCpu, uint32_t cMaxInstructions, uint32_t cPollRate, uint32_t *pcInstructions);
-VMM_INT_DECL(VBOXSTRICTRC)  IEMExecRecompiler(PVMCC pVM, PVMCPUCC pVCpu);
+VMM_INT_DECL(VBOXSTRICTRC)  IEMExecRecompiler(PVMCC pVM, PVMCPUCC pVCpu, bool fWasHalted);
 /** Statistics returned by IEMExecForExits. */
 typedef struct IEMEXECFOREXITSTATS
Index: /trunk/include/VBox/vmm/tm.h
===================================================================
--- /trunk/include/VBox/vmm/tm.h	(revision 105697)
+++ /trunk/include/VBox/vmm/tm.h	(revision 105698)
@@ -273,5 +273,5 @@
 VMMDECL(bool)           TMTimerPollBool(PVMCC pVM, PVMCPUCC pVCpu);
 VMM_INT_DECL(bool)      TMTimerPollBoolWith32BitMilliTS(PVMCC pVM, PVMCPUCC pVCpu, uint32_t *pmsNow);
-VMM_INT_DECL(bool)      TMTimerPollBoolWithNanoTS(PVMCC pVM, PVMCPUCC pVCpu, uint64_t *pnsNow);
+VMM_INT_DECL(uint64_t)  TMTimerPollBoolWithNanoTS(PVMCC pVM, PVMCPUCC pVCpu, uint64_t *pnsNow);
 VMM_INT_DECL(void)      TMTimerPollVoid(PVMCC pVM, PVMCPUCC pVCpu);
 VMM_INT_DECL(uint64_t)  TMTimerPollGIP(PVMCC pVM, PVMCPUCC pVCpu, uint64_t *pu64Delta);
Index: /trunk/src/VBox/VMM/VMMAll/IEMAllN8veRecompBltIn.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/IEMAllN8veRecompBltIn.cpp	(revision 105697)
+++ /trunk/src/VBox/VMM/VMMAll/IEMAllN8veRecompBltIn.cpp	(revision 105698)
@@ -221,7 +221,7 @@
     {
 # ifdef RT_ARCH_AMD64
-        /* dec  [rbx + cIrqChecksTillNextPoll] */
+        /* dec  [rbx + cTbsTillNextTimerPoll] */
         pCodeBuf[off++] = 0xff;
-        off = iemNativeEmitGprByVCpuDisp(pCodeBuf, off, 1, RT_UOFFSETOF(VMCPU, iem.s.cIrqChecksTillNextPoll));
+        off = iemNativeEmitGprByVCpuDisp(pCodeBuf, off, 1, RT_UOFFSETOF(VMCPU, iem.s.cTbsTillNextTimerPoll));
 
         /* jz   ReturnBreakFF */
@@ -229,8 +229,8 @@
 
 # elif defined(RT_ARCH_ARM64)
-        AssertCompile(RTASSERT_OFFSET_OF(VMCPU, iem.s.cIrqChecksTillNextPoll) < _4K * sizeof(uint32_t));
-        off = iemNativeEmitLoadGprFromVCpuU32Ex(pCodeBuf, off, idxTmpReg1, RT_UOFFSETOF(VMCPU, iem.s.cIrqChecksTillNextPoll));
+        AssertCompile(RTASSERT_OFFSET_OF(VMCPU, iem.s.cTbsTillNextTimerPoll) < _4K * sizeof(uint32_t));
+        off = iemNativeEmitLoadGprFromVCpuU32Ex(pCodeBuf, off, idxTmpReg1, RT_UOFFSETOF(VMCPU, iem.s.cTbsTillNextTimerPoll));
         pCodeBuf[off++] = Armv8A64MkInstrSubUImm12(idxTmpReg1, idxTmpReg1, 1, false /*f64Bit*/);
-        off = iemNativeEmitStoreGprToVCpuU32Ex(pCodeBuf, off, idxTmpReg1, RT_UOFFSETOF(VMCPU, iem.s.cIrqChecksTillNextPoll));
+        off = iemNativeEmitStoreGprToVCpuU32Ex(pCodeBuf, off, idxTmpReg1, RT_UOFFSETOF(VMCPU, iem.s.cTbsTillNextTimerPoll));
 
         /* cbz reg1, ReturnBreakFF */
@@ -398,5 +398,5 @@
 
 /**
- * Built-in function that works the cIrqChecksTillNextPoll counter on direct TB
+ * Built-in function that works the cTbsTillNextTimerPoll counter on direct TB
  * linking, like loop-jumps.
  */
Index: /trunk/src/VBox/VMM/VMMAll/IEMAllThrdFuncsBltIn.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/IEMAllThrdFuncsBltIn.cpp	(revision 105697)
+++ /trunk/src/VBox/VMM/VMMAll/IEMAllThrdFuncsBltIn.cpp	(revision 105698)
@@ -199,10 +199,10 @@
 
 /**
- * Built-in function that works the cIrqChecksTillNextPoll counter on direct TB
+ * Built-in function that works the cTbsTillNextTimerPoll counter on direct TB
  * linking, like loop-jumps.
  */
 IEM_DECL_IEMTHREADEDFUNC_DEF(iemThreadedFunc_BltIn_CheckTimers)
 {
-    if (RT_LIKELY(--pVCpu->iem.s.cIrqChecksTillNextPoll > 0))
+    if (RT_LIKELY(--pVCpu->iem.s.cTbsTillNextTimerPoll > 0))
         return VINF_SUCCESS;
 
@@ -219,5 +219,5 @@
 IEM_DECL_IEMTHREADEDFUNC_DEF(iemThreadedFunc_BltIn_CheckTimersAndIrq)
 {
-    if (RT_LIKELY(--pVCpu->iem.s.cIrqChecksTillNextPoll > 0))
+    if (RT_LIKELY(--pVCpu->iem.s.cTbsTillNextTimerPoll > 0))
         return iemThreadedFunc_BltIn_CheckIrqCommon(pVCpu);
 
Index: /trunk/src/VBox/VMM/VMMAll/IEMAllThrdRecompiler.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/IEMAllThrdRecompiler.cpp	(revision 105697)
+++ /trunk/src/VBox/VMM/VMMAll/IEMAllThrdRecompiler.cpp	(revision 105698)
@@ -2920,4 +2920,46 @@
 *********************************************************************************************************************************/
 
+/** Default TB factor.
+ * This is basically the number of nanoseconds we guess executing a TB takes
+ * on average.  We estimates it high if we can.
+ * @note Best if this is a power of two so it can be translated to a shift.  */
+#define IEM_TIMER_POLL_DEFAULT_FACTOR   UINT32_C(64)
+/** The minimum number of nanoseconds we can allow between timer pollings.
+ * This must take the cost of TMTimerPollBoolWithNanoTS into mind.  We put that
+ * cost at 104 ns now, thus this constant is at 256 ns. */
+#define IEM_TIMER_POLL_MIN_NS           UINT32_C(256)
+/** The IEM_TIMER_POLL_MIN_NS value roughly translated to TBs, with some grains
+ * of salt thrown in.
+ * The idea is that we will be able to make progress with guest code execution
+ * before polling timers and between running timers. */
+#define IEM_TIMER_POLL_MIN_ITER         UINT32_C(12)
+/** The maximum number of nanoseconds we can allow between timer pollings.
+ * This probably shouldn't be too high, as we don't have any timer
+ * reprogramming feedback in the polling code.  So, when a device reschedule a
+ * timer for an earlier delivery, we won't know about it.  */
+#define IEM_TIMER_POLL_MAX_NS           UINT32_C(8388608) /* 0x800000 ns = 8.4 ms */
+/** The IEM_TIMER_POLL_MAX_NS value roughly translated to TBs, with some grains
+ * of salt thrown in.
+ * This helps control fluctuations in the NU benchmark. */
+#define IEM_TIMER_POLL_MAX_ITER         _512K
+
+
+DECL_FORCE_INLINE(uint32_t) iemPollTimersCalcDefaultCountdown(uint64_t cNsDelta)
+{
+    if (cNsDelta >= IEM_TIMER_POLL_MAX_NS)
+        return RT_MIN(IEM_TIMER_POLL_MAX_NS / IEM_TIMER_POLL_DEFAULT_FACTOR, IEM_TIMER_POLL_MAX_ITER);
+
+    cNsDelta = RT_BIT_64(ASMBitFirstSetU32(cNsDelta) - 1); /* round down to power of 2 */
+    uint32_t const cRet = cNsDelta / IEM_TIMER_POLL_DEFAULT_FACTOR;
+    if (cRet >= IEM_TIMER_POLL_MIN_ITER)
+    {
+        if (cRet <= IEM_TIMER_POLL_MAX_ITER)
+            return cRet;
+        return IEM_TIMER_POLL_MAX_ITER;
+    }
+    return IEM_TIMER_POLL_MIN_ITER;
+}
+
+
 /**
  * Helper for polling timers.
@@ -2925,12 +2967,59 @@
 DECLHIDDEN(int) iemPollTimers(PVMCC pVM, PVMCPUCC pVCpu) RT_NOEXCEPT
 {
-    /*
-     * Do the polling and calculate the time since the last time.
-     */
-    uint64_t       nsNow        = 0;
-    bool const     fExpired     = TMTimerPollBoolWithNanoTS(pVM, pVCpu, &nsNow);
+    STAM_PROFILE_START(&pVCpu->iem.s.StatTimerPoll, a);
+
+    /*
+     * Check for VM_FF_TM_VIRTUAL_SYNC and call TMR3VirtualSyncFF if set.
+     * This is something all EMTs can do.
+     */
+    /* If the virtual sync FF is set, respond to it. */
+    bool fRanTimers = VM_FF_IS_SET(pVM, VM_FF_TM_VIRTUAL_SYNC);
+    if (!fRanTimers)
+    { /* likely */ }
+    else
+    {
+        STAM_PROFILE_START(&pVCpu->iem.s.StatTimerPollRun, b);
+        TMR3VirtualSyncFF(pVM, pVCpu);
+        STAM_PROFILE_STOP(&pVCpu->iem.s.StatTimerPollRun, b);
+    }
+
+    /*
+     * Poll timers.
+     *
+     * On the 10980xe the polling averaging 314 ticks, with a min of 201, while
+     * running a norton utilities DOS benchmark program. TSC runs at 3GHz,
+     * translating that to 104 ns and 67 ns respectively. (An M2 booting win11
+     * has an average of 2 ticks / 84 ns.)
+     *
+     * With the same setup the TMR3VirtualSyncFF and else branch here profiles
+     * to 79751 ticks / 26583 ns on average, with a min of 1194 ticks / 398 ns.
+     * (An M2 booting win11 has an average of 24 ticks / 1008 ns, with a min of
+     * 8 ticks / 336 ns.)
+     *
+     * If we get a zero return value we run timers.  Non-timer EMTs shouldn't
+     * ever see a zero value here, so we just call TMR3TimerQueuesDo.  However,
+     * we do not re-run timers if we already called TMR3VirtualSyncFF above, we
+     * try to make sure some code is executed first.
+     */
+    uint64_t nsNow    = 0;
+    uint64_t cNsDelta = TMTimerPollBoolWithNanoTS(pVM, pVCpu, &nsNow);
+    if (cNsDelta >= 1) /* It is okay to run virtual sync timers a little early. */
+    { /* likely */ }
+    else if (!fRanTimers || VM_FF_IS_SET(pVM, VM_FF_TM_VIRTUAL_SYNC))
+    {
+        STAM_PROFILE_START(&pVCpu->iem.s.StatTimerPollRun, b);
+        TMR3TimerQueuesDo(pVM);
+        fRanTimers = true;
+        nsNow = 0;
+        cNsDelta = TMTimerPollBoolWithNanoTS(pVM, pVCpu, &nsNow);
+        STAM_PROFILE_STOP(&pVCpu->iem.s.StatTimerPollRun, b);
+    }
+    else
+        cNsDelta = 33;
+
+    /*
+     * Calc interval and update the timestamps.
+     */
     uint64_t const cNsSinceLast = nsNow - pVCpu->iem.s.nsRecompilerPollNow;
-
-    /* Store the new timstamps.  */
     pVCpu->iem.s.nsRecompilerPollNow = nsNow;
     pVCpu->iem.s.msRecompilerPollNow = (uint32_t)(nsNow / RT_NS_1MS);
@@ -2944,30 +3033,105 @@
      * on the guest code.
      */
-/** @todo can we make this even more adaptive based on current timer config as well? */
-    uint32_t       cIrqChecksTillNextPoll = pVCpu->iem.s.cIrqChecksTillNextPollPrev;
-    uint32_t const cNsIdealPollInterval   = pVCpu->iem.s.cNsIdealPollInterval;
-    int64_t const  nsFromIdeal            = cNsSinceLast - cNsIdealPollInterval;
+#ifdef IEM_WITH_ADAPTIVE_TIMER_POLLING
+    uint32_t cItersTillNextPoll = pVCpu->iem.s.cTbsTillNextTimerPollPrev;
+    if (cNsDelta >= RT_NS_1SEC / 4)
+    {
+        /*
+         * Non-timer EMTs should end up here with a fixed 500ms delta, just return
+         * the max and keep the polling over head to the deadicated timer EMT.
+         */
+        AssertCompile(IEM_TIMER_POLL_MAX_ITER * IEM_TIMER_POLL_DEFAULT_FACTOR <= RT_NS_100MS);
+        cItersTillNextPoll = IEM_TIMER_POLL_MAX_ITER;
+    }
+    else
+    {
+        /*
+         * This is the timer EMT.
+         */
+        if (cNsDelta <= IEM_TIMER_POLL_MIN_NS)
+        {
+            STAM_COUNTER_INC(&pVCpu->iem.s.StatTimerPollTiny);
+            cItersTillNextPoll = IEM_TIMER_POLL_MIN_ITER;
+            IEMTLBTRACE_USER0(pVCpu, TMVirtualSyncGetLag(pVM), RT_MAKE_U64(cNsSinceLast, cNsDelta), cItersTillNextPoll, 0);
+        }
+        else
+        {
+            uint32_t const cNsDeltaAdj   = cNsDelta >= IEM_TIMER_POLL_MAX_NS ? IEM_TIMER_POLL_MAX_NS     : (uint32_t)cNsDelta;
+            uint32_t const cNsDeltaSlack = cNsDelta >= IEM_TIMER_POLL_MAX_NS ? IEM_TIMER_POLL_MAX_NS / 2 : cNsDeltaAdj / 4;
+            if (   cNsSinceLast            < RT_MAX(IEM_TIMER_POLL_MIN_NS, 64)
+                || cItersTillNextPoll < IEM_TIMER_POLL_MIN_ITER /* paranoia */)
+            {
+                STAM_COUNTER_INC(&pVCpu->iem.s.StatTimerPollDefaultCalc);
+                cItersTillNextPoll = iemPollTimersCalcDefaultCountdown(cNsDeltaAdj);
+                IEMTLBTRACE_USER1(pVCpu, TMVirtualSyncGetLag(pVM), RT_MAKE_U64(cNsSinceLast, cNsDelta), cItersTillNextPoll, 3);
+            }
+            else if (   cNsSinceLast >= cNsDeltaAdj + cNsDeltaSlack
+                     || cNsSinceLast <= cNsDeltaAdj - cNsDeltaSlack)
+            {
+                if (cNsSinceLast >= cItersTillNextPoll)
+                {
+                    uint32_t uFactor = (uint32_t)(cNsSinceLast + cItersTillNextPoll - 1) / cItersTillNextPoll;
+                    cItersTillNextPoll = cNsDeltaAdj / uFactor;
+                    STAM_PROFILE_ADD_PERIOD(&pVCpu->iem.s.StatTimerPollFactorDivision, uFactor);
+                    IEMTLBTRACE_USER1(pVCpu, TMVirtualSyncGetLag(pVM), RT_MAKE_U64(cNsSinceLast, cNsDelta), cItersTillNextPoll, 1);
+                }
+                else
+                {
+                    uint32_t uFactor = cItersTillNextPoll / (uint32_t)cNsSinceLast;
+                    cItersTillNextPoll = cNsDeltaAdj * uFactor;
+                    STAM_PROFILE_ADD_PERIOD(&pVCpu->iem.s.StatTimerPollFactorMultiplication, uFactor);
+                    IEMTLBTRACE_USER1(pVCpu, TMVirtualSyncGetLag(pVM), RT_MAKE_U64(cNsSinceLast, cNsDelta), cItersTillNextPoll, 2);
+                }
+
+                if (cItersTillNextPoll >= IEM_TIMER_POLL_MIN_ITER)
+                {
+                    if (cItersTillNextPoll <= IEM_TIMER_POLL_MAX_ITER)
+                    { /* likely */ }
+                    else
+                    {
+                        STAM_COUNTER_INC(&pVCpu->iem.s.StatTimerPollMax);
+                        cItersTillNextPoll = IEM_TIMER_POLL_MAX_ITER;
+                    }
+                }
+                else
+                    cItersTillNextPoll = IEM_TIMER_POLL_MIN_ITER;
+            }
+            else
+            {
+                STAM_COUNTER_INC(&pVCpu->iem.s.StatTimerPollUnchanged);
+                IEMTLBTRACE_USER3(pVCpu, TMVirtualSyncGetLag(pVM), RT_MAKE_U64(cNsSinceLast, cNsDelta), cItersTillNextPoll, 0x00);
+            }
+        }
+        pVCpu->iem.s.cTbsTillNextTimerPollPrev = cItersTillNextPoll;
+    }
+#else
+/** Poll timers every 400 us / 2500 Hz. (source: thin air) */
+# define IEM_TIMER_POLL_IDEAL_NS     (400U * RT_NS_1US)
+    uint32_t       cItersTillNextPoll   = pVCpu->iem.s.cTbsTillNextTimerPollPrev;
+    uint32_t const cNsIdealPollInterval = IEM_TIMER_POLL_IDEAL_NS;
+    int64_t const  nsFromIdeal          = cNsSinceLast - cNsIdealPollInterval;
     if (nsFromIdeal < 0)
     {
-        if ((uint64_t)-nsFromIdeal > cNsIdealPollInterval / 8 && cIrqChecksTillNextPoll < _64K)
-        {
-            cIrqChecksTillNextPoll += cIrqChecksTillNextPoll / 8;
-            pVCpu->iem.s.cIrqChecksTillNextPollPrev = cIrqChecksTillNextPoll;
+        if ((uint64_t)-nsFromIdeal > cNsIdealPollInterval / 8 && cItersTillNextPoll < _64K)
+        {
+            cItersTillNextPoll += cItersTillNextPoll / 8;
+            pVCpu->iem.s.cTbsTillNextTimerPollPrev = cItersTillNextPoll;
         }
     }
     else
     {
-        if ((uint64_t)nsFromIdeal > cNsIdealPollInterval / 8 && cIrqChecksTillNextPoll > 256)
-        {
-            cIrqChecksTillNextPoll -= cIrqChecksTillNextPoll / 8;
-            pVCpu->iem.s.cIrqChecksTillNextPollPrev = cIrqChecksTillNextPoll;
-        }
-    }
-    pVCpu->iem.s.cIrqChecksTillNextPoll = cIrqChecksTillNextPoll;
+        if ((uint64_t)nsFromIdeal > cNsIdealPollInterval / 8 && cItersTillNextPoll > 256)
+        {
+            cItersTillNextPoll -= cItersTillNextPoll / 8;
+            pVCpu->iem.s.cTbsTillNextTimerPollPrev = cItersTillNextPoll;
+        }
+    }
+#endif
+    pVCpu->iem.s.cTbsTillNextTimerPoll = cItersTillNextPoll;
 
     /*
      * Repeat the IRQ and FF checks.
      */
-    if (!fExpired)
+    if (cNsDelta > 0)
     {
         uint32_t fCpu = pVCpu->fLocalForcedActions;
@@ -2981,6 +3145,10 @@
                                   || CPUMIsInInterruptShadow(&pVCpu->cpum.GstCtx)) ) )
                       && !VM_FF_IS_ANY_SET(pVCpu->CTX_SUFF(pVM), VM_FF_ALL_MASK) ))
+        {
+            STAM_PROFILE_STOP(&pVCpu->iem.s.StatTimerPoll, a);
             return VINF_SUCCESS;
-    }
+        }
+    }
+    STAM_PROFILE_STOP(&pVCpu->iem.s.StatTimerPoll, a);
     return VINF_IEM_REEXEC_BREAK_FF;
 }
@@ -3265,5 +3433,5 @@
 
 
-VMM_INT_DECL(VBOXSTRICTRC) IEMExecRecompiler(PVMCC pVM, PVMCPUCC pVCpu)
+VMM_INT_DECL(VBOXSTRICTRC) IEMExecRecompiler(PVMCC pVM, PVMCPUCC pVCpu, bool fWasHalted)
 {
     /*
@@ -3291,8 +3459,21 @@
 #endif
         iemInitExec(pVCpu, 0 /*fExecOpts*/);
-    if (RT_LIKELY(pVCpu->iem.s.msRecompilerPollNow != 0))
+
+    if (RT_LIKELY(!fWasHalted && pVCpu->iem.s.msRecompilerPollNow != 0))
     { }
     else
-        pVCpu->iem.s.msRecompilerPollNow = (uint32_t)(TMVirtualGetNoCheck(pVM) / RT_NS_1MS);
+    {
+        /* Do polling after halt and the first time we get here. */
+#ifdef IEM_WITH_ADAPTIVE_TIMER_POLLING
+        uint64_t       nsNow      = 0;
+        uint32_t const cItersTillPoll = iemPollTimersCalcDefaultCountdown(TMTimerPollBoolWithNanoTS(pVM, pVCpu, &nsNow));
+        pVCpu->iem.s.cTbsTillNextTimerPollPrev = cItersTillPoll;
+        pVCpu->iem.s.cTbsTillNextTimerPoll     = cItersTillPoll;
+#else
+        uint64_t const nsNow = TMVirtualGetNoCheck(pVM);
+#endif
+        pVCpu->iem.s.nsRecompilerPollNow = nsNow;
+        pVCpu->iem.s.msRecompilerPollNow = (uint32_t)(nsNow / RT_NS_1MS);
+    }
     pVCpu->iem.s.ppTbLookupEntryR3 = &pVCpu->iem.s.pTbLookupEntryDummyR3;
 
@@ -3352,5 +3533,5 @@
                     {
                         /* Once in a while we need to poll timers here. */
-                        if ((int32_t)--pVCpu->iem.s.cIrqChecksTillNextPoll > 0)
+                        if ((int32_t)--pVCpu->iem.s.cTbsTillNextTimerPoll > 0)
                         { /* likely */ }
                         else
Index: /trunk/src/VBox/VMM/VMMAll/TMAll.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/TMAll.cpp	(revision 105697)
+++ /trunk/src/VBox/VMM/VMMAll/TMAll.cpp	(revision 105698)
@@ -1155,5 +1155,5 @@
  * free time source for recent use tracking and such.
  *
- * @returns true if timers are pending, false if not.
+ * @returns Nanoseconds till the next event, 0 if event already pending.
  *
  * @param   pVM         The cross context VM structure.
@@ -1162,10 +1162,10 @@
  * @thread  The emulation thread.
  */
-VMM_INT_DECL(bool) TMTimerPollBoolWithNanoTS(PVMCC pVM, PVMCPUCC pVCpu, uint64_t *pnsNow)
+VMM_INT_DECL(uint64_t) TMTimerPollBoolWithNanoTS(PVMCC pVM, PVMCPUCC pVCpu, uint64_t *pnsNow)
 {
     AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
-    uint64_t off = 0;
-    tmTimerPollInternal(pVM, pVCpu, &off, pnsNow);
-    return off == 0;
+    uint64_t offDelta = 0;
+    tmTimerPollInternal(pVM, pVCpu, &offDelta, pnsNow);
+    return offDelta;
 }
 
Index: /trunk/src/VBox/VMM/VMMR3/EM.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/EM.cpp	(revision 105697)
+++ /trunk/src/VBox/VMM/VMMR3/EM.cpp	(revision 105698)
@@ -1057,9 +1057,10 @@
  * @param   pVM         The cross context VM structure.
  * @param   pVCpu       The cross context virtual CPU structure.
+ * @param   fWasHalted  Set if we're comming out of a CPU HALT state.
  * @param   pfFFDone    Where to store an indicator telling whether or not
  *                      FFs were done before returning.
  *
  */
-static VBOXSTRICTRC emR3RecompilerExecute(PVM pVM, PVMCPU pVCpu, bool *pfFFDone)
+static VBOXSTRICTRC emR3RecompilerExecute(PVM pVM, PVMCPU pVCpu, bool fWasHalted, bool *pfFFDone)
 {
     STAM_REL_PROFILE_START(&pVCpu->em.s.StatREMTotal, a);
@@ -1098,5 +1099,5 @@
 #ifdef VBOX_WITH_IEM_RECOMPILER
             if (pVM->em.s.fIemRecompiled)
-                rcStrict = IEMExecRecompiler(pVM, pVCpu);
+                rcStrict = IEMExecRecompiler(pVM, pVCpu, fWasHalted);
             else
 #endif
@@ -1190,4 +1191,9 @@
         }
 
+#ifdef VBOX_WITH_IEM_RECOMPILER
+        fWasHalted = false;
+#else
+        RT_NOREF(fWasHalted);
+#endif
     } /* The Inner Loop, recompiled execution mode version. */
 
@@ -2253,5 +2259,5 @@
                 case VINF_EM_RESUME:
                     Log2(("EMR3ExecuteVM: VINF_EM_RESUME: %d -> VINF_EM_RESCHEDULE\n", enmOldState));
-                    /* Don't reschedule in the halted or wait for SIPI case. */
+                    /* Don't reschedule in the halted or wait-for-SIPI cases. */
                     if (    pVCpu->em.s.enmPrevState == EMSTATE_WAIT_SIPI
                         ||  pVCpu->em.s.enmPrevState == EMSTATE_HALTED)
@@ -2510,5 +2516,5 @@
                  */
                 case EMSTATE_RECOMPILER:
-                    rc = VBOXSTRICTRC_TODO(emR3RecompilerExecute(pVM, pVCpu, &fFFDone));
+                    rc = VBOXSTRICTRC_TODO(emR3RecompilerExecute(pVM, pVCpu, enmOldState == EMSTATE_HALTED, &fFFDone));
                     Log2(("EMR3ExecuteVM: emR3RecompilerExecute -> %Rrc\n", rc));
                     break;
Index: /trunk/src/VBox/VMM/VMMR3/IEMR3.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/IEMR3.cpp	(revision 105697)
+++ /trunk/src/VBox/VMM/VMMR3/IEMR3.cpp	(revision 105698)
@@ -227,8 +227,6 @@
 
 #ifndef VBOX_VMM_TARGET_ARMV8
-        /* Poll timers every 400 us / 2500 Hz. (source: thin air) */
-        pVCpu->iem.s.cNsIdealPollInterval       = 400U * RT_NS_1US;
-        pVCpu->iem.s.cIrqChecksTillNextPoll     = 128;
-        pVCpu->iem.s.cIrqChecksTillNextPollPrev = 128;
+        pVCpu->iem.s.cTbsTillNextTimerPoll      = 128;
+        pVCpu->iem.s.cTbsTillNextTimerPollPrev  = 128;
 #endif
 
@@ -603,6 +601,24 @@
 # endif
 
-        STAMR3RegisterF(pVM, (void *)&pVCpu->iem.s.cIrqChecksTillNextPollPrev, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
-                        "Timer polling interval",                       "/IEM/CPU%u/re/cIrqChecksTillNextPollPrev", idCpu);
+# ifdef VBOX_WITH_STATISTICS
+        STAMR3RegisterF(pVM, (void *)&pVCpu->iem.s.StatTimerPoll, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL,
+                        "Timer polling profiling",                      "/IEM/CPU%u/re/TimerPoll/", idCpu);
+        STAMR3RegisterF(pVM, (void *)&pVCpu->iem.s.StatTimerPollRun, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL,
+                        "Timer polling profiling",                      "/IEM/CPU%u/re/TimerPoll/Running", idCpu);
+        STAMR3RegisterF(pVM, (void *)&pVCpu->iem.s.StatTimerPollUnchanged, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
+                        "Timer polling interval unchanged",             "/IEM/CPU%u/re/TimerPoll/Unchanged", idCpu);
+        STAMR3RegisterF(pVM, (void *)&pVCpu->iem.s.StatTimerPollTiny, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
+                        "Timer polling interval tiny",                  "/IEM/CPU%u/re/TimerPoll/Tiny", idCpu);
+        STAMR3RegisterF(pVM, (void *)&pVCpu->iem.s.StatTimerPollDefaultCalc, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
+                        "Timer polling interval calculated using defaults", "/IEM/CPU%u/re/TimerPoll/DefaultCalc", idCpu);
+        STAMR3RegisterF(pVM, (void *)&pVCpu->iem.s.StatTimerPollMax, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
+                        "Timer polling interval maxed out",             "/IEM/CPU%u/re/TimerPoll/Max", idCpu);
+        STAMR3RegisterF(pVM, (void *)&pVCpu->iem.s.StatTimerPollFactorDivision, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
+                        "Timer polling factor",                         "/IEM/CPU%u/re/TimerPoll/FactorDivision", idCpu);
+        STAMR3RegisterF(pVM, (void *)&pVCpu->iem.s.StatTimerPollFactorMultiplication, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
+                        "Timer polling factor",                         "/IEM/CPU%u/re/TimerPoll/FactorMultiplication", idCpu);
+# endif
+        STAMR3RegisterF(pVM, (void *)&pVCpu->iem.s.cTbsTillNextTimerPollPrev, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
+                        "Timer polling interval (in TBs)",              "/IEM/CPU%u/re/TimerPollInterval", idCpu);
 
         PIEMTBALLOCATOR const pTbAllocator = pVCpu->iem.s.pTbAllocatorR3;
Index: /trunk/src/VBox/VMM/include/IEMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/include/IEMInternal.h	(revision 105697)
+++ /trunk/src/VBox/VMM/include/IEMInternal.h	(revision 105698)
@@ -92,4 +92,11 @@
 # define IEM_WITH_THROW_CATCH
 #endif /*ASM-NOINC-END*/
+
+/** @def IEM_WITH_ADAPTIVE_TIMER_POLLING
+ * Enables the adaptive timer polling code.
+ */
+#if defined(DOXYGEN_RUNNING) || 0
+# define IEM_WITH_ADAPTIVE_TIMER_POLLING
+#endif
 
 /** @def IEM_WITH_INTRA_TB_JUMPS
@@ -821,5 +828,5 @@
 #endif /* !IEM_WITH_TLB_TRACE */
 
-#if defined(IEM_WITH_TLB_TRACE) && defined(IN_RING3)
+#if defined(IEM_WITH_TLB_TRACE) && defined(IN_RING3) && 1
 # define IEMTLBTRACE_INVLPG(a_pVCpu, a_GCPtr) \
     iemTlbTrace(a_pVCpu, kIemTlbTraceType_InvlPg, a_GCPtr)
@@ -2188,14 +2195,11 @@
 
     /** The number of IRQ/FF checks till the next timer poll call. */
-    uint32_t                cIrqChecksTillNextPoll;
+    uint32_t                cTbsTillNextTimerPoll;
     /** The virtual sync time at the last timer poll call in milliseconds. */
     uint32_t                msRecompilerPollNow;
     /** The virtual sync time at the last timer poll call in nanoseconds. */
     uint64_t                nsRecompilerPollNow;
-    /** The previous cIrqChecksTillNextPoll value. */
-    uint32_t                cIrqChecksTillNextPollPrev;
-    /** The ideal nanosecond interval between two timer polls.
-     * @todo make this adaptive?  */
-    uint32_t                cNsIdealPollInterval;
+    /** The previous cTbsTillNextTimerPoll value. */
+    uint32_t                cTbsTillNextTimerPollPrev;
 
     /** The current instruction number in a native TB.
@@ -2242,6 +2246,4 @@
      *  currently not up to date in EFLAGS. */
     uint32_t                fSkippingEFlags;
-    /** Spaced reserved for recompiler data / alignment. */
-    uint32_t                u32RecompilerStuff2;
 #if 0  /* unused */
     /** Previous GCPhysInstrBuf value - only valid if fTbCrossedPage is set.   */
@@ -2493,8 +2495,21 @@
     /** @} */
 
+    /** Timer polling statistics (debug only).
+     * @{  */
+    STAMPROFILE             StatTimerPoll;
+    STAMPROFILE             StatTimerPollPoll;
+    STAMPROFILE             StatTimerPollRun;
+    STAMCOUNTER             StatTimerPollUnchanged;
+    STAMCOUNTER             StatTimerPollTiny;
+    STAMCOUNTER             StatTimerPollDefaultCalc;
+    STAMCOUNTER             StatTimerPollMax;
+    STAMPROFILE             StatTimerPollFactorDivision;
+    STAMPROFILE             StatTimerPollFactorMultiplication;
+    /** @} */
+
 #ifdef IEM_WITH_TLB_TRACE
-    uint64_t                au64Padding[6];
+    uint64_t                au64Padding[7];
 #else
-    //uint64_t                au64Padding[1];
+    uint64_t                au64Padding[1];
 #endif
 
