Index: trunk/src/VBox/VMM/VMMAll/EMAll.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMAll/EMAll.cpp	(revision 72579)
+++ trunk/src/VBox/VMM/VMMAll/EMAll.cpp	(revision 72580)
@@ -404,4 +404,38 @@
 
 /**
+ * Worker for EMHistoryExec that checks for ring-3 returns and flags
+ * continuation of the EMHistoryExec run there.
+ */
+DECL_FORCE_INLINE(void) emHistoryExecSetContinueExitRecIdx(PVMCPU pVCpu, VBOXSTRICTRC rcStrict, PCEMEXITREC pExitRec)
+{
+    pVCpu->em.s.idxContinueExitRec = UINT16_MAX;
+#ifdef IN_RING3
+    RT_NOREF_PV(rcStrict); RT_NOREF_PV(pExitRec);
+#else
+    switch (VBOXSTRICTRC_VAL(rcStrict))
+    {
+        case VINF_SUCCESS:
+        default:
+            break;
+
+        /* Only status codes that EMHandleRCTmpl.h will resume EMHistoryExec with. */
+        case VINF_IOM_R3_IOPORT_READ:           /* -> emR3ExecuteIOInstruction */
+        case VINF_IOM_R3_IOPORT_WRITE:          /* -> emR3ExecuteIOInstruction */
+        case VINF_IOM_R3_IOPORT_COMMIT_WRITE:   /* -> VMCPU_FF_IOM -> VINF_EM_RESUME_R3_HISTORY_EXEC -> emR3ExecuteIOInstruction */
+        case VINF_IOM_R3_MMIO_READ:             /* -> emR3ExecuteInstruction */
+        case VINF_IOM_R3_MMIO_WRITE:            /* -> emR3ExecuteInstruction */
+        case VINF_IOM_R3_MMIO_READ_WRITE:       /* -> emR3ExecuteInstruction */
+        case VINF_IOM_R3_MMIO_COMMIT_WRITE:     /* -> VMCPU_FF_IOM -> VINF_EM_RESUME_R3_HISTORY_EXEC -> emR3ExecuteIOInstruction */
+        case VINF_CPUM_R3_MSR_READ:             /* -> emR3ExecuteInstruction */
+        case VINF_CPUM_R3_MSR_WRITE:            /* -> emR3ExecuteInstruction */
+        case VINF_GIM_R3_HYPERCALL:             /* -> emR3ExecuteInstruction */
+            pVCpu->em.s.idxContinueExitRec = (uint16_t)(pExitRec - &pVCpu->em.s.aExitRecords[0]);
+            break;
+    }
+#endif /* !IN_RING3 */
+}
+
+
+/**
  * Execute using history.
  *
@@ -439,4 +473,5 @@
             LogFlow(("EMHistoryExec/EXEC_WITH_MAX: %Rrc cExits=%u cMaxExitDistance=%u cInstructions=%u\n",
                      VBOXSTRICTRC_VAL(rcStrict), ExecStats.cExits, ExecStats.cMaxExitDistance, ExecStats.cInstructions));
+            emHistoryExecSetContinueExitRecIdx(pVCpu, rcStrict, pExitRec);
             return rcStrict;
         }
@@ -456,4 +491,5 @@
             LogFlow(("EMHistoryExec/EXEC_PROBE: %Rrc cExits=%u cMaxExitDistance=%u cInstructions=%u\n",
                      VBOXSTRICTRC_VAL(rcStrict), ExecStats.cExits, ExecStats.cMaxExitDistance, ExecStats.cInstructions));
+            emHistoryExecSetContinueExitRecIdx(pVCpu, rcStrict, pExitRecUnconst);
             if (ExecStats.cExits >= 2)
             {
@@ -463,9 +499,13 @@
                 LogFlow(("EMHistoryExec/EXEC_PROBE: -> EXEC_WITH_MAX %u\n", ExecStats.cMaxExitDistance));
             }
+#ifndef IN_RING3
+            else if (pVCpu->em.s.idxContinueExitRec != UINT16_MAX)
+                LogFlow(("EMHistoryExec/EXEC_PROBE: -> ring-3\n"));
+#endif
             else
             {
                 pExitRecUnconst->enmAction = EMEXITACTION_NORMAL_PROBED;
+                pVCpu->em.s.idxContinueExitRec = UINT16_MAX;
                 LogFlow(("EMHistoryExec/EXEC_PROBE: -> PROBED\n"));
-                /** @todo check for return to ring-3 and such and optimize/reprobe. */
             }
             return rcStrict;
@@ -484,4 +524,7 @@
 
 
+/**
+ * Worker for emHistoryAddOrUpdateRecord.
+ */
 DECL_FORCE_INLINE(PCEMEXITREC) emHistoryRecordInit(PEMEXITREC pExitRec, uint64_t uFlatPC, uint32_t uFlagsAndType, uint64_t uExitNo)
 {
@@ -497,4 +540,7 @@
 
 
+/**
+ * Worker for emHistoryAddOrUpdateRecord.
+ */
 DECL_FORCE_INLINE(PCEMEXITREC) emHistoryRecordInitNew(PVMCPU pVCpu, PEMEXITENTRY pHistEntry, uintptr_t idxSlot,
                                                       PEMEXITREC pExitRec, uint64_t uFlatPC,
@@ -505,4 +551,19 @@
     LogFlow(("emHistoryRecordInitNew: [%#x] = %#07x %016RX64; (%u of %u used)\n", idxSlot, uFlagsAndType, uFlatPC,
              pVCpu->em.s.cExitRecordUsed, RT_ELEMENTS(pVCpu->em.s.aExitRecords) ));
+    return emHistoryRecordInit(pExitRec, uFlatPC, uFlagsAndType, uExitNo);
+}
+
+
+/**
+ * Worker for emHistoryAddOrUpdateRecord.
+ */
+DECL_FORCE_INLINE(PCEMEXITREC) emHistoryRecordInitReplacement(PEMEXITENTRY pHistEntry, uintptr_t idxSlot,
+                                                              PEMEXITREC pExitRec, uint64_t uFlatPC,
+                                                              uint32_t uFlagsAndType, uint64_t uExitNo)
+{
+    pHistEntry->idxSlot = (uint32_t)idxSlot;
+    LogFlow(("emHistoryRecordInitReplacement: [%#x] = %#07x %016RX64 replacing %#07x %016RX64 with %u hits, %u exits old\n",
+             idxSlot, uFlagsAndType, uFlatPC, pExitRec->uFlagsAndType, pExitRec->uFlatPC, pExitRec->cHits,
+             uExitNo - pExitRec->uLastExitNo));
     return emHistoryRecordInit(pExitRec, uFlatPC, uFlagsAndType, uExitNo);
 }
@@ -615,5 +676,5 @@
                 STAM_REL_COUNTER_INC(&pVCpu->em.s.aStatHistoryRecReplaced[iOldestStep]);
                 pExitRec = &pVCpu->em.s.aExitRecords[idxOldest];
-                return emHistoryRecordInitNew(pVCpu, pHistEntry, idxOldest, pExitRec, uFlatPC, uFlagsAndType, uExitNo);
+                return emHistoryRecordInitReplacement(pHistEntry, idxOldest, pExitRec, uFlatPC, uFlagsAndType, uExitNo);
             }
         }
@@ -689,4 +750,5 @@
      */
     if (   (uFlagsAndType & (EMEXIT_F_KIND_MASK | EMEXIT_F_CS_EIP | EMEXIT_F_UNFLATTENED_PC)) == EMEXIT_F_KIND_EM
+        && pVCpu->em.s.fExitOptimizationEnabled
         && uFlatPC != UINT64_MAX)
         return emHistoryAddOrUpdateRecord(pVCpu, uFlagsAndType, uFlatPC, pHistEntry, uExitNo);
@@ -769,4 +831,5 @@
      */
     if (   (uFlagsAndType & (EMEXIT_F_KIND_MASK | EMEXIT_F_CS_EIP | EMEXIT_F_UNFLATTENED_PC)) == EMEXIT_F_KIND_EM
+        && pVCpu->em.s.fExitOptimizationEnabled
         && pHistEntry->uFlatPC != UINT64_MAX)
         return emHistoryAddOrUpdateRecord(pVCpu, uFlagsAndType, pHistEntry->uFlatPC, pHistEntry, uExitNo);
@@ -804,5 +867,6 @@
      * If common exit type, we will insert/update the exit into the exit record hash table.
      */
-    if ((uFlagsAndType & (EMEXIT_F_KIND_MASK | EMEXIT_F_CS_EIP | EMEXIT_F_UNFLATTENED_PC)) == EMEXIT_F_KIND_EM)
+    if (   (uFlagsAndType & (EMEXIT_F_KIND_MASK | EMEXIT_F_CS_EIP | EMEXIT_F_UNFLATTENED_PC)) == EMEXIT_F_KIND_EM
+        && pVCpu->em.s.fExitOptimizationEnabled)
         return emHistoryAddOrUpdateRecord(pVCpu, uFlagsAndType, uFlatPC, pHistEntry, uExitNo);
     return NULL;
Index: trunk/src/VBox/VMM/VMMR3/EM.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/EM.cpp	(revision 72579)
+++ trunk/src/VBox/VMM/VMMR3/EM.cpp	(revision 72580)
@@ -149,6 +149,17 @@
     }
 
-    LogRel(("EMR3Init: fRecompileUser=%RTbool fRecompileSupervisor=%RTbool fRawRing1Enabled=%RTbool fIemExecutesAll=%RTbool fGuruOnTripleFault=%RTbool\n",
-            pVM->fRecompileUser, pVM->fRecompileSupervisor, pVM->fRawRing1Enabled, pVM->em.s.fIemExecutesAll, pVM->em.s.fGuruOnTripleFault));
+    /**
+     * @cfgm{/EM/ExitOptimizationEnabled, bool, true}
+     * Whether to try correlate exit history, detect hot spots and try optimize
+     * these using IEM if there are other exits close by.
+     */
+    bool fExitOptimizationEnabled = true;
+    rc = CFGMR3QueryBoolDef(pCfgEM, "ExitOptimizationEnabled", &fExitOptimizationEnabled, true);
+    AssertLogRelRCReturn(rc, rc);
+    for (VMCPUID i = 0; i < pVM->cCpus; i++)
+        pVM->aCpus[i].em.s.fExitOptimizationEnabled = fExitOptimizationEnabled;
+
+    LogRel(("EMR3Init: fRecompileUser=%RTbool fRecompileSupervisor=%RTbool fRawRing1Enabled=%RTbool fIemExecutesAll=%RTbool fGuruOnTripleFault=%RTbool fExitOptimizationEnabled=%RTbool\n",
+            pVM->fRecompileUser, pVM->fRecompileSupervisor, pVM->fRawRing1Enabled, pVM->em.s.fIemExecutesAll, pVM->em.s.fGuruOnTripleFault, fExitOptimizationEnabled));
 
 #ifdef VBOX_WITH_REM
@@ -175,9 +186,11 @@
         PVMCPU pVCpu = &pVM->aCpus[i];
 
-        pVCpu->em.s.enmState     = (i == 0) ? EMSTATE_NONE : EMSTATE_WAIT_SIPI;
-        pVCpu->em.s.enmPrevState = EMSTATE_NONE;
-        pVCpu->em.s.fForceRAW    = false;
-
-        pVCpu->em.s.pCtx         = CPUMQueryGuestCtxPtr(pVCpu);
+        pVCpu->em.s.enmState            = i == 0 ? EMSTATE_NONE : EMSTATE_WAIT_SIPI;
+        pVCpu->em.s.enmPrevState        = EMSTATE_NONE;
+        pVCpu->em.s.fForceRAW           = false;
+        pVCpu->em.s.u64TimeSliceStart   = 0; /* paranoia */
+        pVCpu->em.s.idxContinueExitRec  = UINT16_MAX;
+
+        pVCpu->em.s.pCtx                = CPUMQueryGuestCtxPtr(pVCpu);
 #ifdef VBOX_WITH_RAW_MODE
         if (VM_IS_RAW_MODE_ENABLED(pVM))
@@ -187,7 +200,4 @@
         }
 #endif
-
-        /* Force reset of the time slice. */
-        pVCpu->em.s.u64TimeSliceStart = 0;
 
 # define EM_REG_COUNTER(a, b, c) \
@@ -1774,5 +1784,13 @@
     /* IOM has pending work (comitting an I/O or MMIO write). */
     if (VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_IOM))
+    {
         rc = IOMR3ProcessForceFlag(pVM, pVCpu, rc);
+        if (pVCpu->em.s.idxContinueExitRec >= RT_ELEMENTS(pVCpu->em.s.aExitRecords))
+        { /* half likely, or at least it's a line shorter. */ }
+        else if (rc == VINF_SUCCESS)
+            rc = VINF_EM_RESUME_R3_HISTORY_EXEC;
+        else
+            pVCpu->em.s.idxContinueExitRec = UINT16_MAX;
+    }
 
 #ifdef VBOX_WITH_RAW_MODE
Index: trunk/src/VBox/VMM/VMMR3/EMR3Nem.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/EMR3Nem.cpp	(revision 72579)
+++ trunk/src/VBox/VMM/VMMR3/EMR3Nem.cpp	(revision 72580)
@@ -179,5 +179,4 @@
     PCPUMCTX pCtx = pVCpu->em.s.pCtx;
 #endif
-    int      rc;
     NOREF(rcRC);
 
@@ -199,10 +198,24 @@
      */
     STAM_PROFILE_START(&pVCpu->em.s.StatIEMEmu, a);
-    CPUM_IMPORT_EXTRN_RET(pVCpu, IEM_CPUMCTX_EXTRN_MUST_MASK);
-    rc = VBOXSTRICTRC_TODO(IEMExecOne(pVCpu));
+
+    VBOXSTRICTRC rcStrict;
+    uint32_t     idxContinueExitRec = pVCpu->em.s.idxContinueExitRec;
+    RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
+    if (idxContinueExitRec >= RT_ELEMENTS(pVCpu->em.s.aExitRecords))
+    {
+        CPUM_IMPORT_EXTRN_RET(pVCpu, IEM_CPUMCTX_EXTRN_MUST_MASK);
+        rcStrict = IEMExecOne(pVCpu);
+    }
+    else
+    {
+        RT_UNTRUSTED_VALIDATED_FENCE();
+        rcStrict = EMHistoryExec(pVCpu, &pVCpu->em.s.aExitRecords[idxContinueExitRec], 0);
+        LogFlow(("emR3NemExecuteInstruction: %Rrc (EMHistoryExec)\n", VBOXSTRICTRC_VAL(rcStrict)));
+    }
+
     STAM_PROFILE_STOP(&pVCpu->em.s.StatIEMEmu, a);
 
-    if (   rc == VERR_IEM_ASPECT_NOT_IMPLEMENTED
-        || rc == VERR_IEM_INSTR_NOT_IMPLEMENTED)
+    if (   rcStrict == VERR_IEM_ASPECT_NOT_IMPLEMENTED
+        || rcStrict == VERR_IEM_INSTR_NOT_IMPLEMENTED)
     {
 #ifdef VBOX_WITH_REM
@@ -215,5 +228,5 @@
         pVM->em.s.idLastRemCpu = pVCpu->idCpu;
 
-        rc = REMR3EmulateInstruction(pVM, pVCpu);
+        rcStrict = REMR3EmulateInstruction(pVM, pVCpu);
         EMRemUnlock(pVM);
         STAM_PROFILE_STOP(&pVCpu->em.s.StatREMEmu, b);
@@ -222,5 +235,5 @@
 #endif /* !VBOX_WITH_REM */
     }
-    return rc;
+    return VBOXSTRICTRC_TODO(rcStrict);
 }
 
@@ -259,28 +272,24 @@
     STAM_PROFILE_START(&pVCpu->em.s.StatIOEmu, a);
 
-#if 0
-    PCPUMCTX pCtx = pVCpu->em.s.pCtx;
-
-    /*
-     * Try to restart the io instruction that was refused in ring-0.
-     */
-    VBOXSTRICTRC rcStrict = HMR3RestartPendingIOInstr(pVM, pVCpu, pCtx);
-    if (IOM_SUCCESS(rcStrict))
-    {
+    /*
+     * Hand it over to the interpreter.
+     */
+    CPUM_IMPORT_EXTRN_RET(pVCpu, IEM_CPUMCTX_EXTRN_MUST_MASK);
+    VBOXSTRICTRC rcStrict;
+    uint32_t     idxContinueExitRec = pVCpu->em.s.idxContinueExitRec;
+    RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
+    if (idxContinueExitRec >= RT_ELEMENTS(pVCpu->em.s.aExitRecords))
+    {
+        rcStrict = IEMExecOne(pVCpu);
+        LogFlow(("emR3NemExecuteIOInstruction: %Rrc (IEMExecOne)\n", VBOXSTRICTRC_VAL(rcStrict)));
+        STAM_COUNTER_INC(&pVCpu->em.s.CTX_SUFF(pStats)->StatIoIem);
+    }
+    else
+    {
+        RT_UNTRUSTED_VALIDATED_FENCE();
+        rcStrict = EMHistoryExec(pVCpu, &pVCpu->em.s.aExitRecords[idxContinueExitRec], 0);
+        LogFlow(("emR3NemExecuteIOInstruction: %Rrc (EMHistoryExec)\n", VBOXSTRICTRC_VAL(rcStrict)));
         STAM_COUNTER_INC(&pVCpu->em.s.CTX_SUFF(pStats)->StatIoRestarted);
-        STAM_PROFILE_STOP(&pVCpu->em.s.StatIOEmu, a);
-        return VBOXSTRICTRC_TODO(rcStrict);     /* rip already updated. */
-    }
-    AssertMsgReturn(rcStrict == VERR_NOT_FOUND, ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)),
-                    RT_SUCCESS_NP(rcStrict) ? VERR_IPE_UNEXPECTED_INFO_STATUS : VBOXSTRICTRC_TODO(rcStrict));
-#endif
-
-    /*
-     * Hand it over to the interpreter.
-     */
-    CPUM_IMPORT_EXTRN_RET(pVCpu, IEM_CPUMCTX_EXTRN_MUST_MASK);
-    VBOXSTRICTRC rcStrict = IEMExecOne(pVCpu);
-    LogFlow(("emR3NemExecuteIOInstruction: %Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
-    STAM_COUNTER_INC(&pVCpu->em.s.CTX_SUFF(pStats)->StatIoIem);
+    }
 
     STAM_PROFILE_STOP(&pVCpu->em.s.StatIOEmu, a);
Index: trunk/src/VBox/VMM/include/EMHandleRCTmpl.h
===================================================================
--- trunk/src/VBox/VMM/include/EMHandleRCTmpl.h	(revision 72579)
+++ trunk/src/VBox/VMM/include/EMHandleRCTmpl.h	(revision 72580)
@@ -217,4 +217,5 @@
         case VINF_IOM_R3_IOPORT_READ:
         case VINF_IOM_R3_IOPORT_WRITE:
+        case VINF_EM_RESUME_R3_HISTORY_EXEC: /* Resume EMHistoryExec after VMCPU_FF_IOM. */
             rc = emR3ExecuteIOInstruction(pVM, pVCpu);
             break;
Index: trunk/src/VBox/VMM/include/EMInternal.h
===================================================================
--- trunk/src/VBox/VMM/include/EMInternal.h	(revision 72579)
+++ trunk/src/VBox/VMM/include/EMInternal.h	(revision 72580)
@@ -400,5 +400,6 @@
 #endif
 
-    /** Pointer to the guest CPUM state. (R3 Ptr) */
+    /** Pointer to the guest CPUM state. (R3 Ptr)
+     * @obsolete Use pVCpu->cpum.GstCtx!  */
     R3PTRTYPE(PCPUMCTX)     pCtx;
 
@@ -499,4 +500,6 @@
 #endif
 
+    /** Exit history table (6KB). */
+    EMEXITENTRY             aExitHistory[256];
     /** Where to store the next exit history entry.
      * Since aExitHistory is 256 items longs, we'll just increment this and
@@ -504,7 +507,13 @@
      * wrapped around or not.  */
     uint64_t                iNextExit;
-    /** Exit history table (6KB). */
-    EMEXITENTRY             aExitHistory[256];
-
+    /** Whether exit optimizations are enabled or not. */
+    bool                    fExitOptimizationEnabled;
+    /** Explicit padding. */
+    bool                    afPadding2[1];
+    /** Index into aExitRecords set by EMHistoryExec when returning to ring-3.
+     * This is UINT16_MAX if not armed.  */
+    uint16_t volatile       idxContinueExitRec;
+    /** Number of exit records in use. */
+    uint32_t                cExitRecordUsed;
     /** Hit statistics for each lookup step. */
     STAMCOUNTER             aStatHistoryRecHits[16];
@@ -515,8 +524,5 @@
     /** New record statistics for each lookup step. */
     STAMCOUNTER             aStatHistoryRecNew[16];
-    /** Number of exit records in use. */
-    uint32_t                cExitRecordUsed;
-    /** Explicit padding. */
-    uint32_t                uPadding2;
+
     /** Exit records (32KB). (Aligned on 32 byte boundrary.) */
     EMEXITREC               aExitRecords[1024];
