VirtualBox

Changeset 105877 in vbox


Ignore:
Timestamp:
Aug 27, 2024 11:17:09 PM (3 months ago)
Author:
vboxsync
Message:

VMM/IEM: Don't flush PC prior to 64-bit relative jumps, flush it when in the #GP(0) code path. bugref:10720 bugref:10373

Location:
trunk/src/VBox/VMM
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/VMM/VMMAll/IEMAllN8veRecompFuncs.h

    r105856 r105877  
    767767 * @param   off             The code buffer offset.
    768768 * @param   idxAddrReg      The host register with the address to check.
    769  * @param   idxOldPcReg     Register holding the old PC that offPc is relative
    770  *                          to if available, otherwise UINT8_MAX.
     769 * @param   offDisp         The relative displacement that has already been
     770 *                          added to idxAddrReg and must be subtracted if
     771 *                          raising a \#GP(0).
    771772 * @param   idxInstr        The current instruction.
    772  * @tparam  a_fAbsolute     Not sure why we have this yet.
    773773 */
    774 template<bool const a_fAbsolute>
    775774DECL_FORCE_INLINE_THROW(uint32_t)
    776 iemNativeEmitCheckGprCanonicalMaybeRaiseGp0WithOldPc(PIEMRECOMPILERSTATE pReNative, uint32_t off,
    777                                                      uint8_t idxAddrReg, uint8_t idxOldPcReg, uint8_t idxInstr)
     775iemNativeEmitCheckGprCanonicalMaybeRaiseGp0WithDisp(PIEMRECOMPILERSTATE pReNative, uint32_t off,
     776                                                    uint8_t idxAddrReg, int64_t offDisp, uint8_t idxInstr)
    778777{
    779778#ifdef IEMNATIVE_WITH_DELAYED_REGISTER_WRITEBACK
    780779    Assert(pReNative->Core.bmGstRegShadowDirty == 0);
    781 #endif
    782 
    783 #ifdef IEMNATIVE_WITH_INSTRUCTION_COUNTING
    784 # ifdef IEMNATIVE_WITH_DELAYED_PC_UPDATING
    785     if (!pReNative->Core.offPc)
    786 # endif
    787         off = iemNativeEmitStoreImmToVCpuU8(pReNative, off, idxInstr, RT_UOFFSETOF(VMCPUCC, iem.s.idxTbCurInstr));
    788 #else
    789     RT_NOREF(idxInstr);
    790780#endif
    791781
     
    822812#endif
    823813
     814    /* Jump to the #GP code (hoping static prediction considers forward branches as not-taken). */
     815    uint32_t const offFixup1 = off;
     816    off = iemNativeEmitJnzToFixed(pReNative, off, off /*8-bit jump suffices*/);
     817
     818    /* jump .Lnoexcept;  Skip the #GP code.  */
     819    uint32_t const offFixup2 = off;
     820    off = iemNativeEmitJmpToFixed(pReNative, off, off /*8-bit jump suffices*/);
     821
     822    /* .Lraisexcpt: */
     823    iemNativeFixupFixedJump(pReNative, offFixup1, off);
     824#ifdef IEMNATIVE_WITH_INSTRUCTION_COUNTING
     825    off = iemNativeEmitStoreImmToVCpuU8(pReNative, off, idxInstr, RT_UOFFSETOF(VMCPUCC, iem.s.idxTbCurInstr), iTmpReg);
     826#else
     827    RT_NOREF(idxInstr);
     828#endif
     829
     830    /* Undo the PC adjustment and store the old PC value. */
     831    off = iemNativeEmitSubGprImm(pReNative, off, idxAddrReg, offDisp, iTmpReg);
     832    off = iemNativeEmitStoreGprToVCpuU64(pReNative, off, idxAddrReg, RT_UOFFSETOF(VMCPU, cpum.GstCtx.rip));
     833
     834    off = iemNativeEmitTbExit(pReNative, off, kIemNativeLabelType_RaiseGp0, false /*fActuallyExitingTb*/);
     835
     836    /* .Lnoexcept: */
     837    iemNativeFixupFixedJump(pReNative, offFixup2, off);
     838
     839    iemNativeRegFreeTmp(pReNative, iTmpReg);
     840    return off;
     841}
     842
     843
     844/**
     845 * Emits code to check if the content of @a idxAddrReg is a canonical address,
     846 * raising a \#GP(0) if it isn't.
     847 *
     848 * Caller makes sure everything is flushed, except maybe PC.
     849 *
     850 * @returns New code buffer offset, UINT32_MAX on failure.
     851 * @param   pReNative       The native recompile state.
     852 * @param   off             The code buffer offset.
     853 * @param   idxAddrReg      The host register with the address to check.
     854 * @param   idxOldPcReg     Register holding the old PC that offPc is relative
     855 *                          to if available, otherwise UINT8_MAX.
     856 * @param   idxInstr        The current instruction.
     857 */
     858DECL_FORCE_INLINE_THROW(uint32_t)
     859iemNativeEmitCheckGprCanonicalMaybeRaiseGp0WithOldPc(PIEMRECOMPILERSTATE pReNative, uint32_t off,
     860                                                     uint8_t idxAddrReg, uint8_t idxOldPcReg, uint8_t idxInstr)
     861{
     862#ifdef IEMNATIVE_WITH_DELAYED_REGISTER_WRITEBACK
     863    Assert(pReNative->Core.bmGstRegShadowDirty == 0);
     864#endif
     865
     866#ifdef IEMNATIVE_WITH_INSTRUCTION_COUNTING
     867# ifdef IEMNATIVE_WITH_DELAYED_PC_UPDATING
     868    if (!pReNative->Core.offPc)
     869# endif
     870        off = iemNativeEmitStoreImmToVCpuU8(pReNative, off, idxInstr, RT_UOFFSETOF(VMCPUCC, iem.s.idxTbCurInstr));
     871#else
     872    RT_NOREF(idxInstr);
     873#endif
     874
     875#ifdef RT_ARCH_AMD64
     876    /*
     877     * if ((((uint32_t)(a_u64Addr >> 32) + UINT32_C(0x8000)) >> 16) != 0)
     878     *     return raisexcpt();
     879     * ---- this variant avoid loading a 64-bit immediate, but is an instruction longer.
     880     */
     881    uint8_t const iTmpReg = iemNativeRegAllocTmp(pReNative, &off);
     882
     883    off = iemNativeEmitLoadGprFromGpr(pReNative, off, iTmpReg, idxAddrReg);
     884    off = iemNativeEmitShiftGprRight(pReNative, off, iTmpReg, 32);
     885    off = iemNativeEmitAddGpr32Imm(pReNative, off, iTmpReg, (int32_t)0x8000);
     886    off = iemNativeEmitShiftGprRight(pReNative, off, iTmpReg, 16);
     887
     888#elif defined(RT_ARCH_ARM64)
     889    /*
     890     * if ((((uint64_t)(a_u64Addr) + UINT64_C(0x800000000000)) >> 48) != 0)
     891     *     return raisexcpt();
     892     * ----
     893     *     mov     x1, 0x800000000000
     894     *     add     x1, x0, x1
     895     *     cmp     xzr, x1, lsr 48
     896     *     b.ne    .Lraisexcpt
     897     */
     898    uint8_t const iTmpReg = iemNativeRegAllocTmp(pReNative, &off);
     899
     900    off = iemNativeEmitLoadGprImm64(pReNative, off, iTmpReg, UINT64_C(0x800000000000));
     901    off = iemNativeEmitAddTwoGprs(pReNative, off, iTmpReg, idxAddrReg);
     902    off = iemNativeEmitCmpArm64(pReNative, off, ARMV8_A64_REG_XZR, iTmpReg, true /*f64Bit*/, 48 /*cShift*/, kArmv8A64InstrShift_Lsr);
     903#else
     904# error "Port me"
     905#endif
     906
    824907#ifdef IEMNATIVE_WITH_DELAYED_PC_UPDATING
    825908    if (pReNative->Core.offPc)
     
    832915        off = iemNativeEmitJzToFixed(pReNative, off, off + 16 /*8-bit suffices*/);
    833916
    834         /* Raising a GP(0), but first we need to update cpum.GstCtx.rip. */
     917        /* .Lraisexcpt: */
     918# ifdef IEMNATIVE_WITH_INSTRUCTION_COUNTING
     919        off = iemNativeEmitStoreImmToVCpuU8(pReNative, off, idxInstr, RT_UOFFSETOF(VMCPUCC, iem.s.idxTbCurInstr), iTmpReg);
     920# endif
     921        /* We need to update cpum.GstCtx.rip. */
    835922        if (idxOldPcReg == UINT8_MAX)
    836923        {
     
    840927        off = iemNativeEmitAddGprImm(pReNative, off, idxOldPcReg, pReNative->Core.offPc);
    841928        off = iemNativeEmitStoreGprToVCpuU64(pReNative, off, idxOldPcReg, RT_UOFFSETOF(VMCPU, cpum.GstCtx.rip));
    842 # ifdef IEMNATIVE_WITH_INSTRUCTION_COUNTING
    843         off = iemNativeEmitStoreImmToVCpuU8(pReNative, off, idxInstr, RT_UOFFSETOF(VMCPUCC, iem.s.idxTbCurInstr));
    844 # endif
     929
    845930        off = iemNativeEmitTbExit(pReNative, off, kIemNativeLabelType_RaiseGp0, false /*fActuallyExitingTb*/);
    846931        iemNativeFixupFixedJump(pReNative, offFixup, off);
     
    911996 *                          to if available, otherwise UINT8_MAX.
    912997 * @param   idxInstr        The current instruction.
    913  * @tparam  a_fAbsolute     Not sure why we have this yet.
    914998 */
    915 template<bool const a_fAbsolute>
    916999DECL_FORCE_INLINE_THROW(uint32_t)
    9171000iemNativeEmitCheckGpr32AgainstCsSegLimitMaybeRaiseGp0WithOldPc(PIEMRECOMPILERSTATE pReNative, uint32_t off,
     
    10491132    if (a_fWithinPage && enmEffOpSize == IEMMODE_64BIT)
    10501133    {
     1134        /* No #GP checking required, just update offPc and get on with it. */
    10511135        pReNative->Core.offPc += (int64_t)offDisp + cbInstr;
    10521136# ifdef IEMNATIVE_WITH_DELAYED_PC_UPDATING_DEBUG
     
    10571141#endif
    10581142    {
    1059         /* We speculatively modify PC and may raise #GP(0), so make sure the right values are in CPUMCTX. */
    1060         off = iemNativeRegFlushPendingWrites(pReNative, off);
    1061 #ifdef IEMNATIVE_WITH_DELAYED_PC_UPDATING
    1062         Assert(pReNative->Core.offPc == 0);
    1063 #endif
     1143        /* Flush all but PC iff we're doing a 64-bit update here and this isn't within a page.. */
     1144        if (RT_LIKELY(enmEffOpSize == IEMMODE_64BIT && !a_fWithinPage))
     1145            off = iemNativeRegFlushPendingWrites(pReNative, off, RT_BIT_64(kIemNativeGstReg_Pc) /*fGstShwExcept*/);
     1146
    10641147        /* Allocate a temporary PC register. */
    1065         uint8_t const idxPcReg = iemNativeRegAllocTmpForGuestReg(pReNative, &off, kIemNativeGstReg_Pc, kIemNativeGstRegUse_ForUpdate);
     1148        uint8_t const idxPcReg = iemNativeRegAllocTmpForGuestReg(pReNative, &off, kIemNativeGstReg_Pc,
     1149                                                                 kIemNativeGstRegUse_ForUpdate);
    10661150
    10671151        /* Perform the addition. */
     
    10731157               We can skip this if the target is within the same page. */
    10741158            if (!a_fWithinPage)
    1075                 off = iemNativeEmitCheckGprCanonicalMaybeRaiseGp0(pReNative, off, idxPcReg, idxInstr);
     1159                off = iemNativeEmitCheckGprCanonicalMaybeRaiseGp0WithDisp(pReNative, off, idxPcReg,
     1160                                                                          (int64_t)offDisp + cbInstr, idxInstr);
    10761161        }
    10771162        else
     
    10811166            off = iemNativeEmitClear16UpGpr(pReNative, off, idxPcReg);
    10821167        }
    1083 #ifdef IEMNATIVE_WITH_DELAYED_PC_UPDATING_DEBUG
     1168
     1169#ifdef IEMNATIVE_WITH_DELAYED_PC_UPDATING
     1170# ifdef IEMNATIVE_WITH_DELAYED_PC_UPDATING_DEBUG
    10841171        off = iemNativeEmitPcDebugAdd(pReNative, off, (int64_t)offDisp + cbInstr, enmEffOpSize == IEMMODE_64BIT ? 64 : 16);
    10851172        off = iemNativeEmitPcDebugCheckWithReg(pReNative, off, idxPcReg);
     1173# endif
     1174        /* Since we've already got the new PC value in idxPcReg, we can just as
     1175           well write it out and reset offPc to zero.  Otherwise, we'd need to use
     1176           a copy the shadow PC, which will cost another move instruction here. */
     1177        uint8_t const idxOldInstrPlusOne = pReNative->idxInstrPlusOneOfLastPcUpdate;
     1178        pReNative->idxInstrPlusOneOfLastPcUpdate = RT_MAX(idxInstr + 1, idxOldInstrPlusOne);
     1179        uint8_t const cInstrsSkipped     = idxInstr <= idxOldInstrPlusOne ? 0 : idxInstr - idxOldInstrPlusOne;
     1180        Log4(("iemNativeEmitRip64RelativeJumpAndFinishingNoFlags: offPc=%#RX64 -> 0; off=%#x; idxInstr=%u cInstrsSkipped=%u cCondDepth=%d\n",
     1181              pReNative->Core.offPc, off, idxInstr, cInstrsSkipped, pReNative->cCondDepth));
     1182        STAM_COUNTER_ADD(&pReNative->pVCpu->iem.s.StatNativePcUpdateDelayed, cInstrsSkipped);
     1183#  ifdef IEMNATIVE_WITH_TB_DEBUG_INFO
     1184        iemNativeDbgInfoAddNativeOffset(pReNative, off);
     1185        iemNativeDbgInfoAddDelayedPcUpdate(pReNative, pReNative->Core.offPc, cInstrsSkipped);
     1186#  endif
     1187        pReNative->Core.offPc = 0;
    10861188#endif
    10871189
     
    13801482    {
    13811483        if (f64Bit)
    1382             off = iemNativeEmitCheckGprCanonicalMaybeRaiseGp0WithOldPc<true>(pReNative, off, idxPcReg, idxOldPcReg, idxInstr);
     1484            off = iemNativeEmitCheckGprCanonicalMaybeRaiseGp0WithOldPc(pReNative, off, idxPcReg, idxOldPcReg, idxInstr);
    13831485        else
    1384             off = iemNativeEmitCheckGpr32AgainstCsSegLimitMaybeRaiseGp0WithOldPc<true>(pReNative, off, idxPcReg,
    1385                                                                                        idxOldPcReg, idxInstr);
     1486            off = iemNativeEmitCheckGpr32AgainstCsSegLimitMaybeRaiseGp0WithOldPc(pReNative, off, idxPcReg, idxOldPcReg, idxInstr);
    13861487    }
    13871488
  • trunk/src/VBox/VMM/VMMAll/IEMAllN8veRecompiler.cpp

    r105856 r105877  
    20722072    pReNative->Core.offPc                  = 0;
    20732073# if defined(IEMNATIVE_WITH_TB_DEBUG_INFO) || defined(VBOX_WITH_STATISTICS)
    2074     pReNative->Core.idxInstrPlusOneOfLastPcUpdate = 0;
     2074    pReNative->idxInstrPlusOneOfLastPcUpdate = 0;
    20752075# endif
    20762076# ifdef IEMNATIVE_WITH_DELAYED_PC_UPDATING_DEBUG
     
    27222722    PIEMTBDBGENTRY const pEntry = iemNativeDbgInfoAddNewEntry(pReNative, pReNative->pDbgInfo);
    27232723    pEntry->DelayedPcUpdate.uType         = kIemTbDbgEntryType_DelayedPcUpdate;
     2724    pEntry->DelayedPcUpdate.cInstrSkipped = cInstrSkipped;
    27242725    pEntry->DelayedPcUpdate.offPc         = offPc; /** @todo support larger values */
    2725     pEntry->DelayedPcUpdate.cInstrSkipped = cInstrSkipped;
    27262726}
    27272727# endif
     
    57685768    Log4(("iemNativeEmitPcWritebackSlow: offPc=%#RX64 -> 0; off=%#x\n", pReNative->Core.offPc, off));
    57695769# else
    5770     uint8_t const idxOldInstrPlusOne = pReNative->Core.idxInstrPlusOneOfLastPcUpdate;
     5770    uint8_t const idxOldInstrPlusOne = pReNative->idxInstrPlusOneOfLastPcUpdate;
    57715771    uint8_t       idxCurCall         = pReNative->idxCurCall;
    57725772    uint8_t       idxInstr           = pReNative->pTbOrg->Thrd.paCalls[idxCurCall].idxInstr; /* unreliable*/
    57735773    while (idxInstr == 0 && idxInstr + 1 < idxOldInstrPlusOne && idxCurCall > 0)
    57745774        idxInstr = pReNative->pTbOrg->Thrd.paCalls[--idxCurCall].idxInstr;
    5775     uint8_t const cInstrsSkipped     = idxInstr <= pReNative->Core.idxInstrPlusOneOfLastPcUpdate ? 0
    5776                                      : idxInstr - pReNative->Core.idxInstrPlusOneOfLastPcUpdate;
     5775    pReNative->idxInstrPlusOneOfLastPcUpdate = RT_MAX(idxInstr + 1, idxOldInstrPlusOne);
     5776    uint8_t const cInstrsSkipped     = idxInstr <= idxOldInstrPlusOne ? 0 : idxInstr - idxOldInstrPlusOne;
    57775777    Log4(("iemNativeEmitPcWritebackSlow: offPc=%#RX64 -> 0; off=%#x; idxInstr=%u cInstrsSkipped=%u\n",
    57785778          pReNative->Core.offPc, off, idxInstr, cInstrsSkipped));
    57795779
    5780     pReNative->Core.idxInstrPlusOneOfLastPcUpdate = RT_MAX(idxInstr + 1, pReNative->Core.idxInstrPlusOneOfLastPcUpdate);
    57815780    STAM_COUNTER_ADD(&pReNative->pVCpu->iem.s.StatNativePcUpdateDelayed, cInstrsSkipped);
    57825781
  • trunk/src/VBox/VMM/include/IEMInternal.h

    r105853 r105877  
    14481448        /* kIemTbDbgEntryType_DelayedPcUpdate. */
    14491449        uint32_t    uType         : 4;
     1450        /** Number of instructions skipped. */
     1451        uint32_t    cInstrSkipped : 8;
    14501452        /* The instruction offset added to the program counter. */
    1451         uint32_t    offPc         : 14;
    1452         /** Number of instructions skipped. */
    1453         uint32_t    cInstrSkipped : 14;
     1453        int32_t     offPc         : 20;
    14541454    } DelayedPcUpdate;
    14551455#endif
  • trunk/src/VBox/VMM/include/IEMN8veRecompiler.h

    r105863 r105877  
    12581258     * as long as possible. */
    12591259    int64_t                     offPc;
    1260 # if defined(IEMNATIVE_WITH_TB_DEBUG_INFO) || defined(VBOX_WITH_STATISTICS)
    1261     /** Statistics: The idxInstr+1 value at the last PC update. */
    1262     uint8_t                     idxInstrPlusOneOfLastPcUpdate;
    1263 # endif
    12641260# ifdef IEMNATIVE_WITH_DELAYED_PC_UPDATING_DEBUG
    12651261    /** Set after we've loaded PC into uPcUpdatingDebug at the first update. */
     
    14631459#endif
    14641460
     1461#if defined(IEMNATIVE_WITH_TB_DEBUG_INFO) || defined(VBOX_WITH_STATISTICS)
     1462    /** Statistics: The idxInstr+1 value at the last PC update. */
     1463    uint8_t                     idxInstrPlusOneOfLastPcUpdate;
     1464#endif
     1465
    14651466#ifdef IEMNATIVE_WITH_TB_DEBUG_INFO
    14661467    /** Number of debug info entries allocated for pDbgInfo. */
    14671468    uint32_t                    cDbgInfoAlloc;
    1468     uint32_t                    uPadding;
    14691469    /** Debug info. */
    14701470    PIEMTBDBG                   pDbgInfo;
  • trunk/src/VBox/VMM/include/IEMN8veRecompilerEmit.h

    r105856 r105877  
    10591059 */
    10601060DECL_INLINE_THROW(uint32_t)
    1061 iemNativeEmitStoreImmToVCpuU8(PIEMRECOMPILERSTATE pReNative, uint32_t off, uint8_t bImm, uint32_t offVCpu)
     1061iemNativeEmitStoreImmToVCpuU8(PIEMRECOMPILERSTATE pReNative, uint32_t off, uint8_t bImm, uint32_t offVCpu,
     1062                              uint8_t idxRegTmp = UINT8_MAX)
    10621063{
    10631064#ifdef RT_ARCH_AMD64
     
    10681069    pbCodeBuf[off++] = bImm;
    10691070    IEMNATIVE_ASSERT_INSTR_BUF_ENSURE(pReNative, off);
     1071    RT_NOREF(idxRegTmp);
    10701072
    10711073#elif defined(RT_ARCH_ARM64)
    10721074    /* Cannot use IEMNATIVE_REG_FIXED_TMP0 for the immediate as that's used by iemNativeEmitGprByVCpuLdSt. */
    1073     uint8_t const idxRegImm = iemNativeRegAllocTmpImm(pReNative, &off, bImm);
    1074     off = iemNativeEmitGprByVCpuLdSt(pReNative, off, idxRegImm, offVCpu, kArmv8A64InstrLdStType_St_Byte, sizeof(uint8_t));
    1075     iemNativeRegFreeTmpImm(pReNative, idxRegImm);
     1075    if (idxRegTmp != UINT8_MAX)
     1076    {
     1077        Assert(idxRegTmp != IEMNATIVE_REG_FIXED_TMP0);
     1078        off = iemNativeEmitLoadGprImm32(pReNative, off, idxRegTmp, bImm);
     1079        off = iemNativeEmitGprByVCpuLdSt(pReNative, off, idxRegTmp, offVCpu, kArmv8A64InstrLdStType_St_Byte, sizeof(uint8_t));
     1080    }
     1081    else
     1082    {
     1083        uint8_t const idxRegImm = iemNativeRegAllocTmpImm(pReNative, &off, bImm);
     1084        off = iemNativeEmitGprByVCpuLdSt(pReNative, off, idxRegImm, offVCpu, kArmv8A64InstrLdStType_St_Byte, sizeof(uint8_t));
     1085        iemNativeRegFreeTmpImm(pReNative, idxRegImm);
     1086    }
    10761087
    10771088#else
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette