Index: /trunk/include/VBox/err.h
===================================================================
--- /trunk/include/VBox/err.h	(revision 100693)
+++ /trunk/include/VBox/err.h	(revision 100694)
@@ -2503,4 +2503,7 @@
 /** Recompiled execution: Stop execution TB - Mode (fExec) changed. */
 #define VINF_IEM_REEXEC_MODE_CHANGED                (5310)
+/** Recompilation: End translation block. */
+#define VINF_IEM_RECOMPILE_END_TB                   (5311)
+
 /** Restart the current instruction. For testing only. */
 #define VERR_IEM_RESTART_INSTRUCTION                (-5389)
Index: /trunk/src/VBox/VMM/Makefile.kmk
===================================================================
--- /trunk/src/VBox/VMM/Makefile.kmk	(revision 100693)
+++ /trunk/src/VBox/VMM/Makefile.kmk	(revision 100694)
@@ -249,5 +249,6 @@
  VBoxVMM_SOURCES += \
  	VMMAll/IEMAllThreadedRecompiler.cpp \
- 	VMMAll/IEMAllThreadedFunctions.cpp
+ 	VMMAll/IEMAllThreadedFunctions.cpp \
+ 	VMMAll/IEMAllThreadedFunctionsBltIn.cpp
 endif
 
Index: /trunk/src/VBox/VMM/VMMAll/IEMAll.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/IEMAll.cpp	(revision 100693)
+++ /trunk/src/VBox/VMM/VMMAll/IEMAll.cpp	(revision 100694)
@@ -997,5 +997,5 @@
                 {
                     Log(("iemOpcodeFetchMoreBytes: %04x:%08RX64 LB %#x + %#zx -> #GP(0)\n",
-                         pVCpu->cpum.GstCtx.cs, pVCpu->cpum.GstCtx.rip, cbInstr, cbDst));
+                         pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, cbInstr, cbDst));
                     iemRaiseGeneralProtectionFault0Jmp(pVCpu);
                 }
@@ -1007,4 +1007,5 @@
                 pVCpu->iem.s.GCPhysInstrBuf   = pTlbe->GCPhys;
                 pVCpu->iem.s.pbInstrBuf       = pTlbe->pbMappingR3;
+                pVCpu->iem.s.fTbCrossedPage  |= offPg == 0;
                 memcpy(pvDst, &pTlbe->pbMappingR3[offPg], cbDst);
                 return;
@@ -1060,5 +1061,5 @@
             {
                 Log(("iemOpcodeFetchMoreBytes: %04x:%08RX64 LB %#x + %#zx -> #GP(0) [slow]\n",
-                     pVCpu->cpum.GstCtx.cs, pVCpu->cpum.GstCtx.rip, cbInstr, cbDst));
+                     pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, cbInstr, cbDst));
                 iemRaiseGeneralProtectionFault0Jmp(pVCpu);
             }
@@ -1098,4 +1099,5 @@
             pVCpu->iem.s.uInstrBufPc      = GCPtrFirst & ~(RTGCPTR)X86_PAGE_OFFSET_MASK;
             pVCpu->iem.s.pbInstrBuf       = NULL;
+            pVCpu->iem.s.fTbCrossedPage  |= offPg == 0;
             if (cbToRead == cbDst)
                 return;
@@ -1185,5 +1187,5 @@
     {
         Log(("iemOpcodeFetchMoreBytes: %04x:%08RX64 LB %#x + %#zx -> #GP(0)\n",
-             pVCpu->cpum.GstCtx.cs, pVCpu->cpum.GstCtx.rip, offOpcode, cbMin));
+             pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, offOpcode, cbMin));
         return iemRaiseGeneralProtectionFault0(pVCpu);
     }
Index: /trunk/src/VBox/VMM/VMMAll/IEMAllThreadedFunctions.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/IEMAllThreadedFunctions.cpp	(revision 100693)
+++ /trunk/src/VBox/VMM/VMMAll/IEMAllThreadedFunctions.cpp	(revision 100694)
@@ -610,37 +610,4 @@
 
 
-
-/**
- * Built-in function that compares the fExec mask against uParam0.
- */
-static IEM_DECL_IMPL_DEF(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckMode,
-                         (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2))
-{
-    uint32_t const fExpectedExec = (uint32_t)uParam0;
-    if (pVCpu->iem.s.fExec == fExpectedExec)
-        return VINF_SUCCESS;
-    Log12(("Mode changed at %04x:%08RX64: %#x -> %#x (xor: %#x)\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip,
-           fExpectedExec, pVCpu->iem.s.fExec, fExpectedExec ^ pVCpu->iem.s.fExec));
-    RT_NOREF(uParam1, uParam2);
-    return VINF_IEM_REEXEC_MODE_CHANGED;
-}
-
-
-/**
- * Built-in function that checks the EIP/IP + uParam0 is within CS.LIM,
- * raising a \#GP(0) if this isn't the case.
- */
-static IEM_DECL_IMPL_DEF(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckCsLim,
-                         (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2))
-{
-    uint32_t const cbInstr = (uint32_t)uParam0;
-    if (pVCpu->cpum.GstCtx.eip - pVCpu->cpum.GstCtx.cs.u32Limit >= cbInstr)
-        return VINF_SUCCESS;
-    Log(("EIP out of bounds at %04x:%08RX32 LB %u - CS.LIM=%#RX32\n",
-         pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.eip, cbInstr, pVCpu->cpum.GstCtx.cs.u32Limit));
-    RT_NOREF(uParam1, uParam2);
-    return iemRaiseGeneralProtectionFault0(pVCpu);
-}
-
 /*
  * The threaded functions.
Index: /trunk/src/VBox/VMM/VMMAll/IEMAllThreadedFunctionsBltIn.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/IEMAllThreadedFunctionsBltIn.cpp	(revision 100694)
+++ /trunk/src/VBox/VMM/VMMAll/IEMAllThreadedFunctionsBltIn.cpp	(revision 100694)
@@ -0,0 +1,403 @@
+/* $Id$ */
+/** @file
+ * IEM - Instruction Decoding and Emulation, Built-in Threaded Functions.
+ *
+ * This is separate from IEMThreadedFunctions.cpp because it doesn't work
+ * with IEM_WITH_OPAQUE_DECODER_STATE defined.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_IEM_RE_THREADED
+#define VMCPU_INCL_CPUM_GST_CTX
+#include <VBox/vmm/iem.h>
+#include <VBox/vmm/cpum.h>
+#include <VBox/vmm/apic.h>
+#include <VBox/vmm/pdm.h>
+#include <VBox/vmm/pgm.h>
+#include <VBox/vmm/iom.h>
+#include <VBox/vmm/em.h>
+#include <VBox/vmm/hm.h>
+#include <VBox/vmm/nem.h>
+#include <VBox/vmm/gim.h>
+#ifdef VBOX_WITH_NESTED_HWVIRT_SVM
+# include <VBox/vmm/em.h>
+# include <VBox/vmm/hm_svm.h>
+#endif
+#ifdef VBOX_WITH_NESTED_HWVIRT_VMX
+# include <VBox/vmm/hmvmxinline.h>
+#endif
+#include <VBox/vmm/tm.h>
+#include <VBox/vmm/dbgf.h>
+#include <VBox/vmm/dbgftrace.h>
+#include "IEMInternal.h"
+#include <VBox/vmm/vmcc.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/param.h>
+#include <VBox/dis.h>
+#include <VBox/disopcode-x86-amd64.h>
+#include <iprt/asm-math.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/x86.h>
+
+#include "IEMInline.h"
+
+
+
+static VBOXSTRICTRC iemThreadeFuncWorkerObsoleteTb(PVMCPUCC pVCpu)
+{
+    iemThreadedTbObsolete(pVCpu, pVCpu->iem.s.pCurTbR3);
+    return VINF_IEM_REEXEC_MODE_CHANGED; /** @todo different status code... */
+}
+
+
+
+/**
+ * Built-in function that compares the fExec mask against uParam0.
+ */
+IEM_DECL_IMPL_DEF(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckMode,
+                  (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2))
+{
+    uint32_t const fExpectedExec = (uint32_t)uParam0;
+    if (pVCpu->iem.s.fExec == fExpectedExec)
+        return VINF_SUCCESS;
+    LogFlow(("Mode changed at %04x:%08RX64: %#x -> %#x (xor: %#x)\n", pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip,
+             fExpectedExec, pVCpu->iem.s.fExec, fExpectedExec ^ pVCpu->iem.s.fExec));
+    RT_NOREF(uParam1, uParam2);
+    return VINF_IEM_REEXEC_MODE_CHANGED;
+}
+
+
+DECL_FORCE_INLINE(RTGCPHYS) iemTbGetRangePhysPageAddr(PCIEMTB pTb, uint8_t idxRange)
+{
+    Assert(idxRange < RT_MIN(pTb->cRanges, RT_ELEMENTS(pTb->aRanges)));
+    uint8_t const idxPage = pTb->aRanges[idxRange].idxPhysPage;
+    Assert(idxPage <= RT_ELEMENTS(pTb->aGCPhysPages));
+    if (idxPage == 0)
+        return pTb->GCPhysPc & ~(RTGCPHYS)GUEST_PAGE_OFFSET_MASK;
+    Assert(!(pTb->aGCPhysPages[idxPage - 1] & GUEST_PAGE_OFFSET_MASK));
+    return pTb->aGCPhysPages[idxPage - 1];
+}
+
+
+/**
+ * Macro that implements the 16/32-bit CS.LIM check, as this is done by a
+ * number of functions.
+ */
+#define BODY_CHECK_CS_LIM(a_cbInstr) do { \
+        if (RT_LIKELY(pVCpu->cpum.GstCtx.eip - pVCpu->cpum.GstCtx.cs.u32Limit >= cbInstr)) \
+        { /* likely */ } \
+        else \
+        { \
+            Log7(("EIP out of bounds at %04x:%08RX32 LB %u - CS.LIM=%#RX32\n", \
+                  pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.eip, (a_cbInstr), pVCpu->cpum.GstCtx.cs.u32Limit)); \
+            return iemRaiseGeneralProtectionFault0(pVCpu); \
+        } \
+    } while(0)
+
+/**
+ * Macro that implements opcode (re-)checking.
+ */
+#define BODY_CHECK_OPCODES(a_pTb, a_idxRange, a_offRange, a_cbInstr) do { \
+        Assert((a_idxRange) < (a_pTb)->cRanges && (a_pTb)->cRanges < RT_ELEMENTS((a_pTb)->aRanges)); \
+        Assert((a_offRange) < (a_pTb)->aRanges[(a_idxRange)].cbOpcodes); \
+        /* We can use pbInstrBuf here as it will be updated when branching (and prior to executing a TB). */ \
+        if (RT_LIKELY(memcmp(&pVCpu->iem.s.pbInstrBuf[(a_pTb)->aRanges[(a_idxRange)].offPhysPage + (a_offRange)], \
+                             &(a_pTb)->pabOpcodes[    (a_pTb)->aRanges[(a_idxRange)].offOpcodes  + (a_offRange)], \
+                                                      (a_pTb)->aRanges[(a_idxRange)].cbOpcodes   - (a_offRange)) == 0)) \
+        { /* likely */ } \
+        else \
+        { \
+            Log7(("TB obsolete: %p at %04x:%08RX64 LB %u; range %u, off %#x LB %#x + %#x; #%u\n", (a_pTb), \
+                  pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, (a_cbInstr), (a_idxRange), \
+                  (a_pTb)->aRanges[(a_idxRange)].offOpcodes, (a_pTb)->aRanges[(a_idxRange)].cbOpcodes, (a_offRange), __LINE__)); \
+            RT_NOREF(a_cbInstr); \
+            return iemThreadeFuncWorkerObsoleteTb(pVCpu); \
+        } \
+    } while(0)
+
+/**
+ * Macro that implements TLB loading and updating pbInstrBuf updating for an
+ * instruction crossing into a new page.
+ *
+ * This may long jump if we're raising a \#PF, \#GP or similar trouble.
+ */
+#define BODY_LOAD_TLB_FOR_NEW_PAGE(a_pTb, a_offInstr, a_idxRange, a_cbInstr) do { \
+        pVCpu->iem.s.pbInstrBuf       = NULL; \
+        pVCpu->iem.s.offCurInstrStart = GUEST_PAGE_SIZE - (a_offInstr); \
+        pVCpu->iem.s.offInstrNextByte = GUEST_PAGE_SIZE; \
+        iemOpcodeFetchBytesJmp(pVCpu, 0, NULL); \
+        \
+        RTGCPHYS const GCPhysNewPage = iemTbGetRangePhysPageAddr(a_pTb, a_idxRange); \
+        if (RT_LIKELY(   pVCpu->iem.s.GCPhysInstrBuf == GCPhysNewPage \
+                      && pVCpu->iem.s.pbInstrBuf)) \
+        { /* likely */ } \
+        else \
+        { \
+            Log7(("TB obsolete: %p at %04x:%08RX64 LB %u; crossing at %#x; GCPhys=%RGp expected %RGp, pbInstrBuf=%p - #%u\n", \
+                  (a_pTb), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, (a_cbInstr), (a_offInstr), \
+                  pVCpu->iem.s.GCPhysInstrBuf, GCPhysNewPage, pVCpu->iem.s.pbInstrBuf, __LINE__)); \
+            RT_NOREF(a_cbInstr); \
+            return iemThreadeFuncWorkerObsoleteTb(pVCpu); \
+        } \
+    } while(0)
+
+/**
+ * Macro that implements TLB loading and updating pbInstrBuf updating when
+ * branching or when crossing a page on an instruction boundrary.
+ *
+ * This differs from BODY_LOAD_TLB_FOR_NEW_PAGE in that it will first check if
+ * it is an inter-page branch.
+ *
+ * This may long jump if we're raising a \#PF, \#GP or similar trouble.
+ */
+#define BODY_LOAD_TLB_FOR_BRANCH(a_pTb, a_idxRange, a_cbInstr) do { \
+        /* Is RIP within the current code page? */ \
+        Assert(pVCpu->cpum.GstCtx.cs.u64Base == 0 || !IEM_IS_64BIT_CODE(pVCpu)); \
+        uint64_t const uPc = pVCpu->cpum.GstCtx.rip + pVCpu->cpum.GstCtx.cs.u64Base; \
+        uint64_t const off = uPc - pVCpu->iem.s.uInstrBufPc; \
+        if (off < pVCpu->iem.s.cbInstrBufTotal) \
+            Assert(!(pVCpu->iem.s.GCPhysInstrBuf & GUEST_PAGE_OFFSET_MASK)); \
+        else \
+        { \
+            /* Must translate new RIP. */ \
+            pVCpu->iem.s.pbInstrBuf       = NULL; \
+            pVCpu->iem.s.offCurInstrStart = 0; \
+            pVCpu->iem.s.offInstrNextByte = 0; \
+            iemOpcodeFetchBytesJmp(pVCpu, 0, NULL); \
+            \
+            RTGCPHYS const GCPhysNewPage = iemTbGetRangePhysPageAddr(a_pTb, a_idxRange); \
+            if (RT_LIKELY(   pVCpu->iem.s.GCPhysInstrBuf == GCPhysNewPage \
+                          && pVCpu->iem.s.pbInstrBuf)) \
+            { /* likely */ } \
+            else \
+            { \
+                Log7(("TB obsolete: %p at %04x:%08RX64 LB %u; branching; GCPhys=%RGp expected %RGp, pbInstrBuf=%p - #%u\n", \
+                      (a_pTb), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, (a_cbInstr), \
+                      pVCpu->iem.s.GCPhysInstrBuf, GCPhysNewPage, pVCpu->iem.s.pbInstrBuf, __LINE__)); \
+                RT_NOREF(a_cbInstr); \
+                return iemThreadeFuncWorkerObsoleteTb(pVCpu); \
+            } \
+        } \
+    } while(0)
+
+
+/**
+ * Built-in function that checks the EIP/IP + uParam0 is within CS.LIM,
+ * raising a \#GP(0) if this isn't the case.
+ */
+IEM_DECL_IMPL_DEF(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckCsLim,
+                  (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2))
+{
+    uint32_t const cbInstr = (uint32_t)uParam0;
+    RT_NOREF(uParam1, uParam2);
+    BODY_CHECK_CS_LIM(cbInstr);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Built-in function for re-checking opcodes and CS.LIM after an instruction
+ * that may have modified them.
+ */
+IEM_DECL_IMPL_DEF(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckCsLimAndOpcodes,
+                  (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2))
+{
+    PCIEMTB const  pTb      = pVCpu->iem.s.pCurTbR3;
+    uint32_t const cbInstr  = (uint32_t)uParam0;
+    uint32_t const idxRange = (uint32_t)uParam1;
+    uint32_t const offRange = (uint32_t)uParam2;
+    BODY_CHECK_CS_LIM(cbInstr);
+    BODY_CHECK_OPCODES(pTb, idxRange, offRange, cbInstr);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Built-in function for re-checking opcodes after an instruction that may have
+ * modified them.
+ */
+IEM_DECL_IMPL_DEF(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckOpcodes,
+                  (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2))
+{
+    PCIEMTB const  pTb      = pVCpu->iem.s.pCurTbR3;
+    uint32_t const cbInstr  = (uint32_t)uParam0;
+    uint32_t const idxRange = (uint32_t)uParam1;
+    uint32_t const offRange = (uint32_t)uParam2;
+    BODY_CHECK_OPCODES(pTb, idxRange, offRange, cbInstr);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Built-in function for checking CS.LIM, loading TLB and checking opcodes on
+ * both pages when transitioning to a different code page.
+ *
+ * This is used when the previous instruction requires revalidation of opcodes
+ * bytes and the current instruction stries a page boundrary with opcode bytes
+ * in both the old and new page.
+ *
+ * @see iemThreadedFunc_BltIn_CheckOpcodesAcrossPageLoadingTlb
+ */
+IEM_DECL_IMPL_DEF(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckCsLimAndOpcodesAcrossPageLoadingTlb,
+                  (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2))
+{
+    PCIEMTB const  pTb         = pVCpu->iem.s.pCurTbR3;
+    uint32_t const cbInstr     = (uint32_t)uParam0;
+    uint32_t const cbStartPage = (uint32_t)(uParam0 >> 32);
+    uint32_t const idxRange1   = (uint32_t)uParam1;
+    uint32_t const offRange1   = (uint32_t)uParam2;
+    uint32_t const idxRange2   = idxRange1 + 1;
+    BODY_CHECK_CS_LIM(cbInstr);
+    BODY_CHECK_OPCODES(pTb, idxRange1, offRange1, cbInstr);
+    BODY_LOAD_TLB_FOR_NEW_PAGE(pTb, cbStartPage, idxRange2, cbInstr);
+    BODY_CHECK_OPCODES(pTb, idxRange2, 0, cbInstr);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Built-in function for loading TLB and checking opcodes on both pages when
+ * transitioning to a different code page.
+ *
+ * This is used when the previous instruction requires revalidation of opcodes
+ * bytes and the current instruction stries a page boundrary with opcode bytes
+ * in both the old and new page.
+ *
+ * @see iemThreadedFunc_BltIn_CheckCsLimAndOpcodesAcrossPageLoadingTlb
+ */
+IEM_DECL_IMPL_DEF(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckOpcodesAcrossPageLoadingTlb,
+                  (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2))
+{
+    PCIEMTB const  pTb         = pVCpu->iem.s.pCurTbR3;
+    uint32_t const cbInstr     = (uint32_t)uParam0;
+    uint32_t const cbStartPage = (uint32_t)(uParam0 >> 32);
+    uint32_t const idxRange1   = (uint32_t)uParam1;
+    uint32_t const offRange1   = (uint32_t)uParam2;
+    uint32_t const idxRange2   = idxRange1 + 1;
+    BODY_CHECK_OPCODES(pTb, idxRange1, offRange1, cbInstr);
+    BODY_LOAD_TLB_FOR_NEW_PAGE(pTb, cbStartPage, idxRange2, cbInstr);
+    BODY_CHECK_OPCODES(pTb, idxRange2, 0, cbInstr);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Built-in function for checking CS.LIM, loading TLB and checking opcodes when
+ * transitioning to a different code page.
+ *
+ * The code page transition can either be natural over onto the next page (with
+ * the instruction starting at page offset zero) or by means of branching.
+ *
+ * @see iemThreadedFunc_BltIn_CheckOpcodesLoadingTlb
+ */
+IEM_DECL_IMPL_DEF(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckCsLimAndOpcodesLoadingTlb,
+                  (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2))
+{
+    PCIEMTB const  pTb      = pVCpu->iem.s.pCurTbR3;
+    uint32_t const cbInstr  = (uint32_t)uParam0;
+    uint32_t const idxRange = (uint32_t)uParam1;
+    uint32_t const offRange = (uint32_t)uParam2;
+    BODY_CHECK_CS_LIM(cbInstr);
+    BODY_LOAD_TLB_FOR_BRANCH(pTb, idxRange, cbInstr);
+    BODY_CHECK_OPCODES(pTb, idxRange, offRange, cbInstr);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Built-in function for loading TLB and checking opcodes when transitioning to
+ * a different code page.
+ *
+ * The code page transition can either be natural over onto the next page (with
+ * the instruction starting at page offset zero) or by means of branching.
+ *
+ * @see iemThreadedFunc_BltIn_CheckCsLimAndOpcodesLoadingTlb
+ */
+IEM_DECL_IMPL_DEF(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckOpcodesLoadingTlb,
+                  (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2))
+{
+    PCIEMTB const  pTb      = pVCpu->iem.s.pCurTbR3;
+    uint32_t const cbInstr  = (uint32_t)uParam0;
+    uint32_t const idxRange = (uint32_t)uParam1;
+    uint32_t const offRange = (uint32_t)uParam2;
+    BODY_LOAD_TLB_FOR_BRANCH(pTb, idxRange, cbInstr);
+    BODY_CHECK_OPCODES(pTb, idxRange, offRange, cbInstr);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Built-in function for checking CS.LIM, loading TLB and checking opcodes when
+ * advancing naturally to a different code page.
+ *
+ * Only opcodes on the new page is checked.
+ *
+ * @see iemThreadedFunc_BltIn_CheckOpcodesOnNextPageLoadingTlb
+ */
+IEM_DECL_IMPL_DEF(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckCsLimAndOpcodesOnNextPageLoadingTlb,
+                  (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2))
+{
+    PCIEMTB const  pTb         = pVCpu->iem.s.pCurTbR3;
+    uint32_t const cbInstr     = (uint32_t)uParam0;
+    uint32_t const cbStartPage = (uint32_t)(uParam0 >> 32);
+    uint32_t const idxRange1   = (uint32_t)uParam1;
+    //uint32_t const offRange1   = (uint32_t)uParam2;
+    uint32_t const idxRange2   = idxRange1 + 1;
+    BODY_CHECK_CS_LIM(cbInstr);
+    BODY_LOAD_TLB_FOR_NEW_PAGE(pTb, cbStartPage, idxRange2, cbInstr);
+    BODY_CHECK_OPCODES(pTb, idxRange2, 0, cbInstr);
+    RT_NOREF(uParam2);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Built-in function for loading TLB and checking opcodes when advancing
+ * naturally to a different code page.
+ *
+ * Only opcodes on the new page is checked.
+ *
+ * @see iemThreadedFunc_BltIn_CheckCsLimAndOpcodesOnNextPageLoadingTlb
+ */
+IEM_DECL_IMPL_DEF(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckOpcodesOnNextPageLoadingTlb,
+                  (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2))
+{
+    PCIEMTB const  pTb         = pVCpu->iem.s.pCurTbR3;
+    uint32_t const cbInstr     = (uint32_t)uParam0;
+    uint32_t const cbStartPage = (uint32_t)(uParam0 >> 32);
+    uint32_t const idxRange1   = (uint32_t)uParam1;
+    //uint32_t const offRange1   = (uint32_t)uParam2;
+    uint32_t const idxRange2   = idxRange1 + 1;
+    BODY_LOAD_TLB_FOR_NEW_PAGE(pTb, cbStartPage, idxRange2, cbInstr);
+    BODY_CHECK_OPCODES(pTb, idxRange2, 0, cbInstr);
+    RT_NOREF(uParam2);
+    return VINF_SUCCESS;
+}
+
Index: /trunk/src/VBox/VMM/VMMAll/IEMAllThreadedPython.py
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/IEMAllThreadedPython.py	(revision 100693)
+++ /trunk/src/VBox/VMM/VMMAll/IEMAllThreadedPython.py	(revision 100694)
@@ -1256,9 +1256,17 @@
             '    kIemThreadedFunc_Invalid = 0,',
             '',
-            '    /*'
-            '     * Predefined'
-            '     */'
+            '    /*',
+            '     * Predefined',
+            '     */',
             '    kIemThreadedFunc_CheckMode,',
             '    kIemThreadedFunc_CheckCsLim,',
+            '    kIemThreadedFunc_CheckCsLimAndOpcodes,',
+            '    kIemThreadedFunc_CheckCsLimAndOpcodesAcrossPageLoadingTlb,',
+            '    kIemThreadedFunc_CheckCsLimAndOpcodesLoadingTlb,',
+            '    kIemThreadedFunc_CheckCsLimAndOpcodesOnNextPageLoadingTlb,',
+            '    kIemThreadedFunc_CheckOpcodes,',
+            '    kIemThreadedFunc_CheckOpcodesAcrossPageLoadingTlb,',
+            '    kIemThreadedFunc_CheckOpcodesLoadingTlb,',
+            '    kIemThreadedFunc_CheckOpcodesOnNextPageLoadingTlb,',
         ];
         iThreadedFunction = 1;
@@ -1420,4 +1428,12 @@
                    + '    iemThreadedFunc_BltIn_CheckMode,\n'
                    + '    iemThreadedFunc_BltIn_CheckCsLim,\n'
+                   + '    iemThreadedFunc_BltIn_CheckCsLimAndOpcodes,\n'
+                   + '    iemThreadedFunc_BltIn_CheckCsLimAndOpcodesAcrossPageLoadingTlb,\n'
+                   + '    iemThreadedFunc_BltIn_CheckCsLimAndOpcodesLoadingTlb,\n'
+                   + '    iemThreadedFunc_BltIn_CheckCsLimAndOpcodesOnNextPageLoadingTlb,\n'
+                   + '    iemThreadedFunc_BltIn_CheckOpcodes,\n'
+                   + '    iemThreadedFunc_BltIn_CheckOpcodesAcrossPageLoadingTlb,\n'
+                   + '    iemThreadedFunc_BltIn_CheckOpcodesLoadingTlb,\n'
+                   + '    iemThreadedFunc_BltIn_CheckOpcodesOnNextPageLoadingTlb,\n'
                    );
         iThreadedFunction = 1;
@@ -1453,4 +1469,12 @@
                    + '    "BltIn_CheckMode",\n'
                    + '    "BltIn_CheckCsLim",\n'
+                   + '    "BltIn_CheckCsLimAndOpcodes",\n'
+                   + '    "BltIn_CheckCsLimAndOpcodesAcrossPageLoadingTlb",\n'
+                   + '    "BltIn_CheckCsLimAndOpcodesLoadingTlb",\n'
+                   + '    "BltIn_CheckCsLimAndOpcodesOnNextPageLoadingTlb",\n'
+                   + '    "BltIn_CheckOpcodes",\n'
+                   + '    "BltIn_CheckOpcodesAcrossPageLoadingTlb",\n'
+                   + '    "BltIn_CheckOpcodesLoadingTlb",\n'
+                   + '    "BltIn_CheckOpcodesOnNextPageLoadingTlb",\n'
                    );
         iThreadedFunction = 1;
Index: /trunk/src/VBox/VMM/VMMAll/IEMAllThreadedRecompiler.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/IEMAllThreadedRecompiler.cpp	(revision 100693)
+++ /trunk/src/VBox/VMM/VMMAll/IEMAllThreadedRecompiler.cpp	(revision 100694)
@@ -11,9 +11,9 @@
  *      - Level 5  (Log5) : Decoding details. [same as IEM]
  *      - Level 6  (Log6) :
- *      - Level 7  (Log7) :
+ *      - Level 7  (Log7) : TB obsoletion.
  *      - Level 8  (Log8) : TB compilation.
  *      - Level 9  (Log9) : TB exec.
  *      - Level 10 (Log10): TB block lookup.
- *      - Level 11 (Log11): TB block lookup.
+ *      - Level 11 (Log11): TB block lookup details.
  *      - Level 12 (Log12): TB insertion.
  */
@@ -114,88 +114,5 @@
 *   Structures and Typedefs                                                                                                      *
 *********************************************************************************************************************************/
-/**
- * A call for the threaded call table.
- */
-typedef struct IEMTHRDEDCALLENTRY
-{
-    /** The function to call (IEMTHREADEDFUNCS). */
-    uint16_t    enmFunction;
-    uint16_t    uUnused0;
-
-    /** Offset into IEMTB::pabOpcodes. */
-    uint16_t    offOpcode;
-    /** The opcode length. */
-    uint8_t     cbOpcode;
-    uint8_t     uUnused1;
-
-    /** Generic parameters. */
-    uint64_t    auParams[3];
-} IEMTHRDEDCALLENTRY;
-AssertCompileSize(IEMTHRDEDCALLENTRY, sizeof(uint64_t) * 4);
-/** Pointer to a threaded call entry. */
-typedef IEMTHRDEDCALLENTRY       *PIEMTHRDEDCALLENTRY;
-/** Pointer to a const threaded call entry. */
-typedef IEMTHRDEDCALLENTRY const *PCIEMTHRDEDCALLENTRY;
-
-
-
-/**
- * Translation block.
- */
-typedef struct IEMTB
-{
-    /** Next block with the same hash table entry. */
-    PIEMTB volatile     pNext;
-    /** List on the local VCPU for blocks. */
-    RTLISTNODE          LocalList;
-
-    /** @name What uniquely identifies the block.
-     * @{ */
-    RTGCPHYS            GCPhysPc;
-    /** IEMTB_F_XXX (i.e. IEM_F_XXX ++). */
-    uint32_t            fFlags;
-    union
-    {
-        struct
-        {
-            /**< Relevant CS X86DESCATTR_XXX bits. */
-            uint16_t    fAttr;
-        } x86;
-    };
-    /** @} */
-
-    union
-    {
-        struct
-        {
-            /** The call sequence table. */
-            PIEMTHRDEDCALLENTRY paCalls;
-            /** Number of calls in paCalls. */
-            uint16_t            cCalls;
-            /** Number of calls allocated. */
-            uint16_t            cAllocated;
-        } Thrd;
-    };
-
-
-    /** Number of bytes of opcodes stored in pabOpcodes. */
-    uint16_t            cbOpcodes;
-    /** The max storage available in the pabOpcodes block. */
-    uint16_t            cbOpcodesAllocated;
-    /** Pointer to the opcode bytes this block was recompiled from. */
-    uint8_t            *pabOpcodes;
-
-#if 0
-    struct
-    {
-        uint16_t        offOpcodes;
-        uint16_t        cbOpcodes;
-    } aRanges;
-
-    /** Physical pages that the .    */
-    RTGCPHYS            aGCPhysPgs[2];
-#endif
-
-} IEMTB;
+
 
 
@@ -203,4 +120,5 @@
 *   Internal Functions                                                                                                           *
 *********************************************************************************************************************************/
+static bool         iemThreadedCompileBeginEmitCallsComplications(PVMCPUCC pVCpu, PIEMTB pTb);
 static VBOXSTRICTRC iemThreadedTbExec(PVMCPUCC pVCpu, PIEMTB pTb);
 
@@ -225,20 +143,29 @@
 #define IEM_MC2_BEGIN_EMIT_CALLS() \
     { \
-        PIEMTB const pTb = pVCpu->iem.s.pCurTbR3; \
-        AssertMsg(pVCpu->iem.s.offOpcode == IEM_GET_INSTR_LEN(pVCpu), \
-                  ("%u vs %u (%04x:%08RX64)\n", pVCpu->iem.s.offOpcode, IEM_GET_INSTR_LEN(pVCpu), \
+        PIEMTB const  pTb = pVCpu->iem.s.pCurTbR3; \
+        uint8_t const cbInstrMc2 = IEM_GET_INSTR_LEN(pVCpu); \
+        AssertMsg(pVCpu->iem.s.offOpcode == cbInstrMc2, \
+                  ("%u vs %u (%04x:%08RX64)\n", pVCpu->iem.s.offOpcode, cbInstrMc2, \
                   pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip)); \
-        /** @todo check for cross page stuff */ \
-        if (!(pTb->fFlags & IEMTB_F_CS_LIM_CHECKS)) \
+        \
+        /* No page crossing, right? */ \
+        uint16_t const offOpcodeMc2 = pTb->cbOpcodes; \
+        uint8_t const  idxRangeMc2  = pTb->cRanges - 1; \
+        if (   !pVCpu->iem.s.fTbCrossedPage \
+            && !pVCpu->iem.s.fTbCheckOpcodes \
+            && !pVCpu->iem.s.fTbBranched \
+            && !(pTb->fFlags & IEMTB_F_CS_LIM_CHECKS)) \
+        { \
+            /** @todo Custom copy function, given range is 1 thru 15 bytes. */ \
+            memcpy(&pTb->pabOpcodes[offOpcodeMc2], pVCpu->iem.s.abOpcode, pVCpu->iem.s.offOpcode); \
+            pTb->cbOpcodes                       = offOpcodeMc2 + pVCpu->iem.s.offOpcode; \
+            pTb->aRanges[idxRangeMc2].cbOpcodes += cbInstrMc2; \
+            Assert(pTb->cbOpcodes <= pTb->cbOpcodesAllocated); \
+        } \
+        else if (iemThreadedCompileBeginEmitCallsComplications(pVCpu, pTb)) \
         { /* likely */ } \
         else \
-        { \
-            PIEMTHRDEDCALLENTRY const pCall = &pTb->Thrd.paCalls[pTb->Thrd.cCalls++]; \
-            pCall->enmFunction = kIemThreadedFunc_CheckCsLim; \
-            pCall->offOpcode   = pTb->cbOpcodes; \
-            pCall->auParams[0] = pCall->cbOpcode = IEM_GET_INSTR_LEN(pVCpu); \
-            pCall->auParams[1] = 0; \
-            pCall->auParams[2] = 0; \
-        } \
+            return VINF_IEM_RECOMPILE_END_TB; \
+        \
         do { } while (0)
 
@@ -248,6 +175,7 @@
         PIEMTHRDEDCALLENTRY const pCall = &pTb->Thrd.paCalls[pTb->Thrd.cCalls++]; \
         pCall->enmFunction = a_enmFunction; \
-        pCall->offOpcode   = pTb->cbOpcodes; \
-        pCall->cbOpcode    = IEM_GET_INSTR_LEN(pVCpu); \
+        pCall->offOpcode   = offOpcodeMc2; \
+        pCall->cbOpcode    = cbInstrMc2; \
+        pCall->idxRange    = idxRangeMc2; \
         pCall->auParams[0] = 0; \
         pCall->auParams[1] = 0; \
@@ -260,6 +188,7 @@
         PIEMTHRDEDCALLENTRY const pCall = &pTb->Thrd.paCalls[pTb->Thrd.cCalls++]; \
         pCall->enmFunction = a_enmFunction; \
-        pCall->offOpcode   = pTb->cbOpcodes; \
-        pCall->cbOpcode    = IEM_GET_INSTR_LEN(pVCpu); \
+        pCall->offOpcode   = offOpcodeMc2; \
+        pCall->cbOpcode    = cbInstrMc2; \
+        pCall->idxRange    = idxRangeMc2; \
         pCall->auParams[0] = a_uArg0; \
         pCall->auParams[1] = 0; \
@@ -273,6 +202,7 @@
         PIEMTHRDEDCALLENTRY const pCall = &pTb->Thrd.paCalls[pTb->Thrd.cCalls++]; \
         pCall->enmFunction = a_enmFunction; \
-        pCall->offOpcode   = pTb->cbOpcodes; \
-        pCall->cbOpcode    = IEM_GET_INSTR_LEN(pVCpu); \
+        pCall->offOpcode   = offOpcodeMc2; \
+        pCall->cbOpcode    = cbInstrMc2; \
+        pCall->idxRange    = idxRangeMc2; \
         pCall->auParams[0] = a_uArg0; \
         pCall->auParams[1] = a_uArg1; \
@@ -287,6 +217,7 @@
         PIEMTHRDEDCALLENTRY const pCall = &pTb->Thrd.paCalls[pTb->Thrd.cCalls++]; \
         pCall->enmFunction = a_enmFunction; \
-        pCall->offOpcode   = pTb->cbOpcodes; \
-        pCall->cbOpcode    = IEM_GET_INSTR_LEN(pVCpu); \
+        pCall->offOpcode   = offOpcodeMc2; \
+        pCall->cbOpcode    = cbInstrMc2; \
+        pCall->idxRange    = idxRangeMc2; \
         pCall->auParams[0] = a_uArg0; \
         pCall->auParams[1] = a_uArg1; \
@@ -295,4 +226,5 @@
 
 #define IEM_MC2_END_EMIT_CALLS() \
+        pTb->cInstructions++; \
     } while (0)
 
@@ -683,13 +615,24 @@
             if (pTb->pabOpcodes)
             {
-                pTb->Thrd.cAllocated    = cCalls;
-                pTb->cbOpcodesAllocated = cCalls * 16;
-                pTb->Thrd.cCalls        = 0;
-                pTb->cbOpcodes          = 0;
-                pTb->pNext              = NULL;
+                pTb->Thrd.cAllocated        = cCalls;
+                pTb->cbOpcodesAllocated     = cCalls * 16;
+                pTb->Thrd.cCalls            = 0;
+                pTb->cbOpcodes              = 0;
+                pTb->pNext                  = NULL;
                 RTListInit(&pTb->LocalList);
-                pTb->GCPhysPc           = GCPhysPc;
-                pTb->x86.fAttr          = (uint16_t)pVCpu->cpum.GstCtx.cs.Attr.u;
-                pTb->fFlags             = (pVCpu->iem.s.fExec & IEMTB_F_IEM_F_MASK) | fExtraFlags;
+                pTb->GCPhysPc               = GCPhysPc;
+                pTb->x86.fAttr              = (uint16_t)pVCpu->cpum.GstCtx.cs.Attr.u;
+                pTb->fFlags                 = (pVCpu->iem.s.fExec & IEMTB_F_IEM_F_MASK) | fExtraFlags;
+
+                /* Init the first opcode range. */
+                pTb->cRanges                = 1;
+                pTb->aRanges[0].cbOpcodes   = 0;
+                pTb->aRanges[0].offOpcodes  = 0;
+                pTb->aRanges[0].offPhysPage = GCPhysPc & GUEST_PAGE_OFFSET_MASK;
+                pTb->aRanges[0].u2Unused    = 0;
+                pTb->aRanges[0].idxPhysPage = 0;
+                pTb->aGCPhysPages[0]        = NIL_RTGCPHYS;
+                pTb->aGCPhysPages[1]        = NIL_RTGCPHYS;
+
                 pVCpu->iem.s.cTbAllocs++;
                 return pTb;
@@ -746,4 +689,13 @@
     RTMemFree(pTb);
     pVCpu->iem.s.cTbFrees++;
+}
+
+
+/**
+ * Called by opcode verifier functions when they detect a problem.
+ */
+void iemThreadedTbObsolete(PVMCPUCC pVCpu, PIEMTB pTb)
+{
+    iemThreadedTbFree(pVCpu->CTX_SUFF(pVM), pVCpu, pTb);
 }
 
@@ -901,4 +853,7 @@
         pVCpu->iem.s.rcPassUp               = VINF_SUCCESS;
         pVCpu->iem.s.fEndTb                 = false;
+        pVCpu->iem.s.fTbCheckOpcodes        = false;
+        pVCpu->iem.s.fTbBranched            = false;
+        pVCpu->iem.s.fTbCrossedPage         = false;
     }
     else
@@ -907,4 +862,5 @@
         Assert(pVCpu->iem.s.rcPassUp        == VINF_SUCCESS);
         Assert(pVCpu->iem.s.fEndTb          == false);
+        Assert(pVCpu->iem.s.fTbCrossedPage  == false);
     }
 
@@ -992,4 +948,308 @@
 
 
+DECLINLINE(void) iemThreadedCopyOpcodeBytesInline(PCVMCPUCC pVCpu, uint8_t *pbDst, uint8_t cbInstr)
+{
+    switch (cbInstr)
+    {
+        default: AssertMsgFailed(("%#x\n", cbInstr)); RT_FALL_THROUGH();
+        case 15:    pbDst[14] = pVCpu->iem.s.abOpcode[14]; RT_FALL_THROUGH();
+        case 14:    pbDst[13] = pVCpu->iem.s.abOpcode[13]; RT_FALL_THROUGH();
+        case 13:    pbDst[12] = pVCpu->iem.s.abOpcode[12]; RT_FALL_THROUGH();
+        case 12:    pbDst[11] = pVCpu->iem.s.abOpcode[11]; RT_FALL_THROUGH();
+        case 11:    pbDst[10] = pVCpu->iem.s.abOpcode[10]; RT_FALL_THROUGH();
+        case 10:    pbDst[9]  = pVCpu->iem.s.abOpcode[9];  RT_FALL_THROUGH();
+        case 9:     pbDst[8]  = pVCpu->iem.s.abOpcode[8];  RT_FALL_THROUGH();
+        case 8:     pbDst[7]  = pVCpu->iem.s.abOpcode[7];  RT_FALL_THROUGH();
+        case 7:     pbDst[6]  = pVCpu->iem.s.abOpcode[6];  RT_FALL_THROUGH();
+        case 6:     pbDst[5]  = pVCpu->iem.s.abOpcode[5];  RT_FALL_THROUGH();
+        case 5:     pbDst[4]  = pVCpu->iem.s.abOpcode[4];  RT_FALL_THROUGH();
+        case 4:     pbDst[3]  = pVCpu->iem.s.abOpcode[3];  RT_FALL_THROUGH();
+        case 3:     pbDst[2]  = pVCpu->iem.s.abOpcode[2];  RT_FALL_THROUGH();
+        case 2:     pbDst[1]  = pVCpu->iem.s.abOpcode[1];  RT_FALL_THROUGH();
+        case 1:     pbDst[0]  = pVCpu->iem.s.abOpcode[0];  break;
+    }
+}
+
+
+/**
+ * Called by IEM_MC2_BEGIN_EMIT_CALLS() under one of these conditions:
+ *
+ *      - CS LIM check required.
+ *      - Must recheck opcode bytes.
+ *      - Previous instruction branched.
+ *      - TLB load detected, probably due to page crossing.
+ *
+ * @returns true if everything went well, false if we're out of space in the TB
+ *          (e.g. opcode ranges).
+ * @param   pVCpu       The cross context virtual CPU structure of the calling
+ *                      thread.
+ * @param   pTb         The translation block being compiled.
+ */
+static bool iemThreadedCompileBeginEmitCallsComplications(PVMCPUCC pVCpu, PIEMTB pTb)
+{
+    Assert((pVCpu->iem.s.GCPhysInstrBuf & GUEST_PAGE_OFFSET_MASK) == 0);
+
+    /*
+     * Prepare call now, even before we know if can accept the instruction in this TB.
+     * This allows us amending parameters w/o making every case suffer.
+     */
+    uint8_t const             cbInstr   = IEM_GET_INSTR_LEN(pVCpu);
+    uint16_t const            offOpcode = pTb->cbOpcodes;
+    uint8_t                   idxRange  = pTb->cRanges - 1;
+
+    PIEMTHRDEDCALLENTRY const pCall     = &pTb->Thrd.paCalls[pTb->Thrd.cCalls];
+    pCall->offOpcode   = offOpcode;
+    pCall->idxRange    = idxRange;
+    pCall->cbOpcode    = cbInstr;
+    pCall->auParams[0] = cbInstr;
+    pCall->auParams[1] = idxRange;
+    pCall->auParams[2] = offOpcode - pTb->aRanges[idxRange].offOpcodes;
+
+/** @todo check if we require IEMTB_F_CS_LIM_CHECKS for any new page we've
+ *        gotten onto.  If we do, stop */
+
+    /*
+     * Case 1: We've branched (RIP changed).
+     *
+     * Sub-case 1a: Same page, no TLB load, so fTbCrossedPage is false.
+     *         Req: 1 extra range, no extra phys.
+     *
+     * Sub-case 1b: Different page, so TLB load necessary and fTbCrossedPage is true.
+     *         Req: 1 extra range, probably 1 extra phys page entry.
+     *
+     * Sub-case 1c: Different page, so TLB load necessary and fTbCrossedPage is true,
+     *              but in addition we cross into the following page and require
+     *              another TLB load.
+     *         Req: 2 extra ranges, probably 2 extra phys page entries.
+     *
+     * Sub-case 1d: Same page, so no initial TLB load necessary, but we cross into
+     *              the following page and thus fTbCrossedPage is true.
+     *         Req: 2 extra ranges, probably 1 extra phys page entry.
+     *
+     * Note! We do not currently optimize branching to the next instruction (sorry
+     *       32-bit PIC code).  We could maybe do that in the branching code that sets (or not) fTbBranched.
+     */
+    if (pVCpu->iem.s.fTbBranched)
+    {
+AssertFailed(); /** @todo enable including branches in TBs and debug this code. */
+        if (   !pVCpu->iem.s.fTbCrossedPage       /* 1a */
+            || pVCpu->iem.s.offCurInstrStart >= 0 /* 1b */ )
+        {
+            /* 1a + 1b - instruction fully within the branched to page. */
+            Assert(pVCpu->iem.s.offCurInstrStart >= 0);
+            Assert(pVCpu->iem.s.offCurInstrStart + cbInstr <= GUEST_PAGE_SIZE);
+
+            /* Check that we've got a free range. */
+            idxRange += 1;
+            if (idxRange < RT_ELEMENTS(pTb->aRanges))
+            { /* likely */ }
+            else
+                return false;
+            pCall->idxRange    = idxRange;
+            pCall->auParams[1] = idxRange;
+            pCall->auParams[2] = 0;
+
+            /* Check that we've got a free page slot. */
+            AssertCompile(RT_ELEMENTS(pTb->aGCPhysPages) == 2);
+            RTGCPHYS const GCPhysNew = pVCpu->iem.s.GCPhysInstrBuf & ~(RTGCPHYS)GUEST_PAGE_OFFSET_MASK;
+            if ((pTb->GCPhysPc & ~(RTGCPHYS)GUEST_PAGE_OFFSET_MASK) == GCPhysNew)
+                pTb->aRanges[idxRange].idxPhysPage = 0;
+            else if (   pTb->aGCPhysPages[0] == NIL_RTGCPHYS
+                     || pTb->aGCPhysPages[0] == GCPhysNew)
+            {
+                pTb->aGCPhysPages[0] = GCPhysNew;
+                pTb->aRanges[idxRange].idxPhysPage = 1;
+            }
+            else if (   pTb->aGCPhysPages[1] == NIL_RTGCPHYS
+                     || pTb->aGCPhysPages[1] == GCPhysNew)
+            {
+                pTb->aGCPhysPages[1] = GCPhysNew;
+                pTb->aRanges[idxRange].idxPhysPage = 2;
+            }
+            else
+                return false;
+
+            /* Finish setting up the new range. */
+            pTb->aRanges[idxRange].offPhysPage = pVCpu->iem.s.offCurInstrStart;
+            pTb->aRanges[idxRange].offOpcodes  = offOpcode;
+            pTb->aRanges[idxRange].cbOpcodes   = cbInstr;
+            pTb->cRanges++;
+
+            /* Determin which function we need to load & check.
+               Note! For jumps to a new page, we'll set both fTbBranched and
+                     fTbCrossedPage to avoid unnecessary TLB work for intra
+                     page branching */
+            if (pVCpu->iem.s.fTbCrossedPage)
+                pCall->enmFunction = pTb->fFlags & IEMTB_F_CS_LIM_CHECKS
+                                   ? kIemThreadedFunc_CheckCsLimAndOpcodesLoadingTlb
+                                   : kIemThreadedFunc_CheckOpcodesLoadingTlb;
+            else
+                pCall->enmFunction = pTb->fFlags & IEMTB_F_CS_LIM_CHECKS
+                                   ? kIemThreadedFunc_CheckCsLimAndOpcodes
+                                   : kIemThreadedFunc_CheckOpcodes;
+        }
+        else
+        {
+            /* 1c + 1d - instruction crosses pages. */
+            Assert(pVCpu->iem.s.offCurInstrStart < 0);
+            Assert(pVCpu->iem.s.offCurInstrStart + cbInstr > 0);
+
+            /* Lazy bird: Check that this isn't case 1c, since we've already
+                          load the first physical address.  End the TB and
+                          make it a case 2b instead.
+
+                          Hmm. Too much bother to detect, so just do the same
+                          with case 1d as well. */
+#if 0       /** @todo get back to this later when we've got the actual branch code in
+             *        place. */
+            uint8_t const cbStartPage = (uint8_t)-pVCpu->iem.s.offCurInstrStart;
+
+            /* Check that we've got two free ranges. */
+            if (idxRange + 2 < RT_ELEMENTS(pTb->aRanges))
+            { /* likely */ }
+            else
+                return false;
+            idxRange += 1;
+            pCall->idxRange    = idxRange;
+            pCall->auParams[1] = idxRange;
+            pCall->auParams[2] = 0;
+
+            /* ... */
+
+#else
+            return false;
+#endif
+        }
+    }
+
+    /*
+     * Case 2: Page crossing.
+     *
+     * Sub-case 2a: The instruction starts on the first byte in the next page.
+     *
+     * Sub-case 2b: The instruction has opcode bytes in both the current and
+     *              following page.
+     *
+     * Both cases requires a new range table entry and probably a new physical
+     * page entry.  The difference is in which functions to emit and whether to
+     * add bytes to the current range.
+     */
+    else if (pVCpu->iem.s.fTbCrossedPage)
+    {
+        /* Check that we've got a free range. */
+        idxRange += 1;
+        if (idxRange < RT_ELEMENTS(pTb->aRanges))
+        { /* likely */ }
+        else
+            return false;
+
+        /* Check that we've got a free page slot. */
+        AssertCompile(RT_ELEMENTS(pTb->aGCPhysPages) == 2);
+        RTGCPHYS const GCPhysNew = pVCpu->iem.s.GCPhysInstrBuf & ~(RTGCPHYS)GUEST_PAGE_OFFSET_MASK;
+        if ((pTb->GCPhysPc & ~(RTGCPHYS)GUEST_PAGE_OFFSET_MASK) == GCPhysNew)
+            pTb->aRanges[idxRange].idxPhysPage = 0;
+        else if (   pTb->aGCPhysPages[0] == NIL_RTGCPHYS
+                 || pTb->aGCPhysPages[0] == GCPhysNew)
+        {
+            pTb->aGCPhysPages[0] = GCPhysNew;
+            pTb->aRanges[idxRange].idxPhysPage = 1;
+        }
+        else if (   pTb->aGCPhysPages[1] == NIL_RTGCPHYS
+                 || pTb->aGCPhysPages[1] == GCPhysNew)
+        {
+            pTb->aGCPhysPages[1] = GCPhysNew;
+            pTb->aRanges[idxRange].idxPhysPage = 2;
+        }
+        else
+            return false;
+
+        if (((pTb->aRanges[idxRange - 1].offPhysPage + pTb->aRanges[idxRange - 1].cbOpcodes) & GUEST_PAGE_OFFSET_MASK) == 0)
+        {
+            Assert(pVCpu->iem.s.offCurInstrStart == 0);
+            pCall->idxRange    = idxRange;
+            pCall->auParams[1] = idxRange;
+            pCall->auParams[2] = 0;
+
+            /* Finish setting up the new range. */
+            pTb->aRanges[idxRange].offPhysPage = pVCpu->iem.s.offCurInstrStart;
+            pTb->aRanges[idxRange].offOpcodes  = offOpcode;
+            pTb->aRanges[idxRange].cbOpcodes   = cbInstr;
+            pTb->cRanges++;
+
+            /* Determin which function we need to load & check. */
+            pCall->enmFunction = pTb->fFlags & IEMTB_F_CS_LIM_CHECKS
+                               ? kIemThreadedFunc_CheckCsLimAndOpcodesLoadingTlb
+                               : kIemThreadedFunc_CheckOpcodesLoadingTlb;
+        }
+        else
+        {
+            Assert(pVCpu->iem.s.offCurInstrStart < 0);
+            Assert(pVCpu->iem.s.offCurInstrStart + cbInstr > 0);
+            uint8_t const cbStartPage = (uint8_t)-pVCpu->iem.s.offCurInstrStart;
+            pCall->auParams[0] |= (uint64_t)cbStartPage << 32;
+
+            /* We've good. Split the instruction over the old and new range table entries. */
+            pTb->aRanges[idxRange - 1].cbOpcodes += cbStartPage;
+
+            pTb->aRanges[idxRange].offPhysPage    = 0;
+            pTb->aRanges[idxRange].offOpcodes     = offOpcode + cbStartPage;
+            pTb->aRanges[idxRange].cbOpcodes      = cbInstr   - cbStartPage;
+            pTb->cRanges++;
+
+            /* Determin which function we need to load & check. */
+            if (pVCpu->iem.s.fTbCheckOpcodes)
+                pCall->enmFunction = pTb->fFlags & IEMTB_F_CS_LIM_CHECKS
+                                   ? kIemThreadedFunc_CheckCsLimAndOpcodesAcrossPageLoadingTlb
+                                   : kIemThreadedFunc_CheckOpcodesAcrossPageLoadingTlb;
+            else
+                pCall->enmFunction = pTb->fFlags & IEMTB_F_CS_LIM_CHECKS
+                                   ? kIemThreadedFunc_CheckCsLimAndOpcodesOnNextPageLoadingTlb
+                                   : kIemThreadedFunc_CheckOpcodesOnNextPageLoadingTlb;
+        }
+    }
+
+    /*
+     * Regular case: No new range required.
+     */
+    else
+    {
+        Assert(pVCpu->iem.s.fTbCheckOpcodes || (pTb->fFlags & IEMTB_F_CS_LIM_CHECKS));
+        if (pVCpu->iem.s.fTbCheckOpcodes)
+            pCall->enmFunction = pTb->fFlags & IEMTB_F_CS_LIM_CHECKS
+                               ? kIemThreadedFunc_CheckCsLimAndOpcodes
+                               : kIemThreadedFunc_CheckOpcodes;
+        else
+            pCall->enmFunction = kIemThreadedFunc_CheckCsLim;
+
+        iemThreadedCopyOpcodeBytesInline(pVCpu, &pTb->pabOpcodes[offOpcode], cbInstr);
+        pTb->cbOpcodes                    = offOpcode + cbInstr;
+        pTb->aRanges[idxRange].cbOpcodes += cbInstr;
+        Assert(pTb->cbOpcodes <= pTb->cbOpcodesAllocated);
+    }
+
+    /*
+     * Commit the call.
+     */
+    pTb->Thrd.cCalls++;
+
+    /*
+     * Clear state.
+     */
+    pVCpu->iem.s.fTbBranched     = false;
+    pVCpu->iem.s.fTbCrossedPage  = false;
+    pVCpu->iem.s.fTbCheckOpcodes = false;
+
+    /*
+     * Copy opcode bytes.
+     */
+    iemThreadedCopyOpcodeBytesInline(pVCpu, &pTb->pabOpcodes[offOpcode], cbInstr);
+    pTb->cbOpcodes = offOpcode + cbInstr;
+    Assert(pTb->cbOpcodes <= pTb->cbOpcodesAllocated);
+
+    return true;
+}
+
+
+
 /**
  * Compiles a new TB and executes it.
@@ -1045,25 +1305,20 @@
             Assert(cCallsPrev - pTb->Thrd.cCalls < 5);
 
-            memcpy(&pTb->pabOpcodes[pTb->cbOpcodes], pVCpu->iem.s.abOpcode, pVCpu->iem.s.offOpcode);
-            pTb->cbOpcodes += pVCpu->iem.s.offOpcode;
-            Assert(pTb->cbOpcodes <= pTb->cbOpcodesAllocated);
             pVCpu->iem.s.cInstructions++;
         }
-        else if (pTb->Thrd.cCalls > 0)
-        {
-            Log8(("%04x:%08RX64: End TB - %u calls, rc=%d\n", uCsLog, uRipLog, pTb->Thrd.cCalls, VBOXSTRICTRC_VAL(rcStrict)));
-
-            if (cCallsPrev != pTb->Thrd.cCalls)
+        else
+        {
+            Log8(("%04x:%08RX64: End TB - %u instr, %u calls, rc=%d\n",
+                  uCsLog, uRipLog, pTb->cInstructions, pTb->Thrd.cCalls, VBOXSTRICTRC_VAL(rcStrict)));
+            if (rcStrict == VINF_IEM_RECOMPILE_END_TB)
+                rcStrict = VINF_SUCCESS;
+
+            if (pTb->Thrd.cCalls > 0)
             {
-                memcpy(&pTb->pabOpcodes[pTb->cbOpcodes], pVCpu->iem.s.abOpcode, pVCpu->iem.s.offOpcode);
-                pTb->cbOpcodes += pVCpu->iem.s.offOpcode;
-                Assert(pTb->cbOpcodes <= pTb->cbOpcodesAllocated);
-                pVCpu->iem.s.cInstructions++;
+                if (cCallsPrev != pTb->Thrd.cCalls)
+                    pVCpu->iem.s.cInstructions++;
+                break;
             }
-            break;
-        }
-        else
-        {
-            Log8(("%04x:%08RX64: End TB - 0 calls, rc=%d\n", uCsLog, uRipLog, VBOXSTRICTRC_VAL(rcStrict)));
+
             pVCpu->iem.s.pCurTbR3 = NULL;
             iemThreadedTbFree(pVM, pVCpu, pTb);
@@ -1072,9 +1327,11 @@
 
         /* Still space in the TB? */
-        if (pTb->Thrd.cCalls + 5 < pTb->Thrd.cAllocated)
+        if (   pTb->Thrd.cCalls + 5 < pTb->Thrd.cAllocated
+            && pTb->cbOpcodes + 16 <= pTb->cbOpcodesAllocated)
             iemThreadedCompileInitDecoder(pVCpu, true /*fReInit*/);
         else
         {
-            Log8(("%04x:%08RX64: End TB - %u calls - full\n", uCsLog, uRipLog, pTb->Thrd.cCalls));
+            Log8(("%04x:%08RX64: End TB - %u instr, %u calls, %u opcode bytes - full\n",
+                  uCsLog, uRipLog, pTb->cInstructions, pTb->Thrd.cCalls, pTb->cbOpcodes));
             break;
         }
@@ -1115,5 +1372,5 @@
     else
     {
-        Log11(("TB obsolete: %p GCPhys=%RGp\n", pTb, pTb->GCPhysPc));
+        Log7(("TB obsolete: %p GCPhys=%RGp\n", pTb, pTb->GCPhysPc));
         iemThreadedTbFree(pVCpu->pVMR3, pVCpu, pTb);
         return VINF_SUCCESS;
Index: /trunk/src/VBox/VMM/include/IEMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/include/IEMInternal.h	(revision 100693)
+++ /trunk/src/VBox/VMM/include/IEMInternal.h	(revision 100694)
@@ -39,4 +39,5 @@
 
 #include <iprt/setjmp-without-sigmask.h>
+#include <iprt/list.h>
 
 
@@ -536,7 +537,4 @@
 
 
-/** Pointer to a translation block. */
-typedef struct IEMTB *PIEMTB;
-
 /** @name IEM_F_XXX - Execution mode flags (IEMCPU::fExec, IEMTB::fFlags).
  *
@@ -720,4 +718,128 @@
 AssertCompile(  IEM_F_MODE_X86_64BIT              & IEM_F_MODE_X86_PROT_MASK);
 AssertCompile(!(IEM_F_MODE_X86_64BIT              & IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK));
+
+/**
+ * A call for the threaded call table.
+ */
+typedef struct IEMTHRDEDCALLENTRY
+{
+    /** The function to call (IEMTHREADEDFUNCS). */
+    uint16_t    enmFunction;
+    uint16_t    uUnused0;
+
+    /** Offset into IEMTB::pabOpcodes. */
+    uint16_t    offOpcode;
+    /** The opcode length. */
+    uint8_t     cbOpcode;
+    /** Index in to IEMTB::aRanges. */
+    uint8_t     idxRange;
+
+    /** Generic parameters. */
+    uint64_t    auParams[3];
+} IEMTHRDEDCALLENTRY;
+AssertCompileSize(IEMTHRDEDCALLENTRY, sizeof(uint64_t) * 4);
+/** Pointer to a threaded call entry. */
+typedef struct IEMTHRDEDCALLENTRY *PIEMTHRDEDCALLENTRY;
+/** Pointer to a const threaded call entry. */
+typedef IEMTHRDEDCALLENTRY const *PCIEMTHRDEDCALLENTRY;
+
+/**
+ * Translation block.
+ */
+#pragma pack(2) /* to prevent the Thrd structure from being padded unnecessarily */
+typedef struct IEMTB
+{
+    /** Next block with the same hash table entry. */
+    struct IEMTB * volatile pNext;
+    /** List on the local VCPU for blocks. */
+    RTLISTNODE          LocalList;
+
+    /** @name What uniquely identifies the block.
+     * @{ */
+    RTGCPHYS            GCPhysPc;
+    /** IEMTB_F_XXX (i.e. IEM_F_XXX ++). */
+    uint32_t            fFlags;
+    union
+    {
+        struct
+        {
+            /**< Relevant CS X86DESCATTR_XXX bits. */
+            uint16_t    fAttr;
+        } x86;
+    };
+    /** @} */
+
+    /** Number of opcode ranges. */
+    uint8_t             cRanges;
+    /** Statistics: Number of instructions in the block. */
+    uint8_t             cInstructions;
+
+    /** Type specific info. */
+    union
+    {
+        struct
+        {
+            /** The call sequence table. */
+            PIEMTHRDEDCALLENTRY paCalls;
+            /** Number of calls in paCalls. */
+            uint16_t            cCalls;
+            /** Number of calls allocated. */
+            uint16_t            cAllocated;
+        } Thrd;
+    };
+
+    /** Number of bytes of opcodes stored in pabOpcodes. */
+    uint16_t            cbOpcodes;
+    /** The max storage available in the pabOpcodes block. */
+    uint16_t            cbOpcodesAllocated;
+    /** Pointer to the opcode bytes this block was recompiled from. */
+    uint8_t            *pabOpcodes;
+
+    /* --- 64 byte cache line end --- */
+
+    /** Opcode ranges.
+     *
+     * The opcode checkers and maybe TLB loading functions will use this to figure
+     * out what to do.  The parameter will specify an entry and the opcode offset to
+     * start at and the minimum number of bytes to verify (instruction length).
+     *
+     * When VT-x and AMD-V looks up the opcode bytes for an exitting instruction,
+     * they'll first translate RIP (+ cbInstr - 1) to a physical address using the
+     * code TLB (must have a valid entry for that address) and scan the ranges to
+     * locate the corresponding opcodes. Probably.
+     */
+    struct IEMTBOPCODERANGE
+    {
+        /** Offset within pabOpcodes. */
+        uint16_t        offOpcodes;
+        /** Number of bytes. */
+        uint16_t        cbOpcodes;
+        /** The page offset. */
+        RT_GCC_EXTENSION
+        uint16_t        offPhysPage : 12;
+        /** Unused bits. */
+        RT_GCC_EXTENSION
+        uint16_t        u2Unused    :  2;
+        /** Index into GCPhysPc + aGCPhysPages for the physical page address. */
+        RT_GCC_EXTENSION
+        uint16_t        idxPhysPage :  2;
+    } aRanges[8];
+
+    /** Physical pages that this TB covers.
+     * The GCPhysPc w/o page offset is element zero, so starting here with 1. */
+    RTGCPHYS            aGCPhysPages[2];
+} IEMTB;
+#pragma pack()
+AssertCompileMemberOffset(IEMTB, x86, 36);
+AssertCompileMemberOffset(IEMTB, cRanges, 38);
+AssertCompileMemberOffset(IEMTB, Thrd, 40);
+AssertCompileMemberOffset(IEMTB, Thrd.cCalls, 48);
+AssertCompileMemberOffset(IEMTB, cbOpcodes, 52);
+AssertCompileMemberSize(IEMTB, aRanges[0], 6);
+AssertCompileSize(IEMTB, 128);
+/** Pointer to a translation block. */
+typedef IEMTB *PIEMTB;
+/** Pointer to a const translation block. */
+typedef IEMTB const *PCIEMTB;
 
 
@@ -1014,8 +1136,16 @@
     /** Number of TBs executed. */
     uint64_t                cTbExec;
+    /** Whether we need to check the opcode bytes for the current instruction.
+     * This is set by a previous instruction if it modified memory or similar.  */
+    bool                    fTbCheckOpcodes;
+    /** Whether we just branched and need to start a new opcode range and emit code
+     * to do a TLB load and check them again. */
+    bool                    fTbBranched;
+    /** Set when GCPhysInstrBuf is updated because of a page crossing. */
+    bool                    fTbCrossedPage;
     /** Whether to end the current TB. */
     bool                    fEndTb;
     /** Spaced reserved for recompiler data / alignment. */
-    bool                    afRecompilerStuff1[7];
+    bool                    afRecompilerStuff1[4];
     /** @} */
 
@@ -4764,4 +4894,26 @@
 IEM_CIMPL_PROTO_1(iemCImpl_Hypercall, uint16_t, uDisOpcode); /* both */
 
+void            iemThreadedTbObsolete(PVMCPUCC pVCpu, PIEMTB pTb);
+IEM_DECL_IMPL_PROTO(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckMode,
+                    (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2));
+IEM_DECL_IMPL_PROTO(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckCsLim,
+                    (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2));
+IEM_DECL_IMPL_PROTO(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckCsLimAndOpcodes,
+                    (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2));
+IEM_DECL_IMPL_PROTO(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckCsLimAndOpcodesAcrossPageLoadingTlb,
+                    (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2));
+IEM_DECL_IMPL_PROTO(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckCsLimAndOpcodesLoadingTlb,
+                    (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2));
+IEM_DECL_IMPL_PROTO(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckCsLimAndOpcodesOnNextPageLoadingTlb,
+                    (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2));
+IEM_DECL_IMPL_PROTO(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckOpcodes,
+                    (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2));
+IEM_DECL_IMPL_PROTO(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckOpcodesAcrossPageLoadingTlb,
+                    (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2));
+IEM_DECL_IMPL_PROTO(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckOpcodesLoadingTlb,
+                    (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2));
+IEM_DECL_IMPL_PROTO(VBOXSTRICTRC, iemThreadedFunc_BltIn_CheckOpcodesOnNextPageLoadingTlb,
+                    (PVMCPU pVCpu, uint64_t uParam0, uint64_t uParam1, uint64_t uParam2));
+
 
 extern const PFNIEMOP g_apfnIemInterpretOnlyOneByteMap[256];
