- Timestamp:
- Mar 29, 2024 1:57:23 AM (13 months ago)
- svn:sync-xref-src-repo-rev:
- 162521
- Location:
- trunk/src/VBox/VMM
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/VMM/VMMAll/IEMAllN8veRecompiler.cpp
r104112 r104114 148 148 * This is useful for freeing up executable memory among other things. */ 149 149 #define IEMEXECMEM_ALT_SUB_WITH_ALLOC_HEADER 150 /** Use alternative pruning. */ 151 #define IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING 150 152 151 153 … … 328 330 * portion corresponding to an chunk). */ 329 331 uint32_t cBitmapElementsPerChunk; 332 333 #ifdef IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING 334 /** The next chunk to prune in. */ 335 uint32_t idxChunkPrune; 336 /** Where in chunk offset to start pruning at. */ 337 uint32_t offChunkPrune; 338 /** Profiling the pruning code. */ 339 STAMPROFILE StatPruneProf; 340 /** Number of bytes recovered by the pruning. */ 341 STAMPROFILE StatPruneRecovered; 342 #endif 343 330 344 #ifdef VBOX_WITH_STATISTICS 331 345 STAMPROFILE StatAlloc; 332 346 #endif 347 333 348 334 349 #if defined(IN_RING3) && !defined(RT_OS_WINDOWS) … … 373 388 374 389 static int iemExecMemAllocatorGrow(PVMCPUCC pVCpu, PIEMEXECMEMALLOCATOR pExecMemAllocator); 390 391 #ifdef IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING 392 /** 393 * Frees up executable memory when we're out space. 394 * 395 * This is an alternative to iemTbAllocatorFreeupNativeSpace() that frees up 396 * space in a more linear fashion from the allocator's point of view. It may 397 * also defragment if implemented & enabled 398 */ 399 static void iemExecMemAllocatorPrune(PVMCPU pVCpu, PIEMEXECMEMALLOCATOR pExecMemAllocator) 400 { 401 # ifndef IEMEXECMEM_ALT_SUB_WITH_ALLOC_HEADER 402 # error "IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING requires IEMEXECMEM_ALT_SUB_WITH_ALLOC_HEADER" 403 # endif 404 STAM_REL_PROFILE_START(&pExecMemAllocator->StatPruneProf, a); 405 406 /* 407 * Before we can start, we must process delayed frees. 408 */ 409 iemTbAllocatorProcessDelayedFrees(pVCpu, pVCpu->iem.s.pTbAllocatorR3); 410 411 AssertCompile(RT_IS_POWER_OF_TWO(IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SIZE)); 412 413 uint32_t const cbChunk = pExecMemAllocator->cbChunk; 414 AssertReturnVoid(RT_IS_POWER_OF_TWO(cbChunk)); 415 AssertReturnVoid(cbChunk >= _1M && cbChunk <= _256M); /* see iemExecMemAllocatorInit */ 416 417 uint32_t const cChunks = pExecMemAllocator->cChunks; 418 AssertReturnVoid(cChunks == pExecMemAllocator->cMaxChunks); 419 AssertReturnVoid(cChunks >= 1); 420 421 /* 422 * Decide how much to prune. The chunk is is a multiple of two, so we'll be 423 * scanning a multiple of two here as well. 424 */ 425 uint32_t cbToPrune = cbChunk; 426 427 /* Never more than 25%. */ 428 if (cChunks < 4) 429 cbToPrune /= cChunks == 1 ? 4 : 2; 430 431 /* Upper limit. In a debug build a 4MB limit averages out at ~0.6ms per call. */ 432 if (cbToPrune > _4M) 433 cbToPrune = _4M; 434 435 /* 436 * Adjust the pruning chunk and offset accordingly. 437 */ 438 uint32_t idxChunk = pExecMemAllocator->idxChunkPrune; 439 uint32_t offChunk = pExecMemAllocator->offChunkPrune; 440 offChunk &= ~(uint32_t)(IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SIZE - 1U); 441 if (offChunk >= cbChunk) 442 { 443 offChunk = 0; 444 idxChunk += 1; 445 } 446 if (idxChunk >= cChunks) 447 { 448 offChunk = 0; 449 idxChunk = 0; 450 } 451 452 uint32_t const offPruneEnd = RT_MIN(offChunk + cbToPrune, cbChunk); 453 454 /* 455 * Do the pruning. The current approach is the sever kind. 456 */ 457 uint64_t cbPruned = 0; 458 uint8_t * const pbChunk = (uint8_t *)pExecMemAllocator->aChunks[idxChunk].pvChunk; 459 while (offChunk < offPruneEnd) 460 { 461 PIEMEXECMEMALLOCHDR pHdr = (PIEMEXECMEMALLOCHDR)&pbChunk[offChunk]; 462 463 /* Is this the start of an allocation block for TB? (We typically have 464 one allocation at the start of each chunk for the unwind info where 465 pTb is NULL.) */ 466 if ( pHdr->uMagic == IEMEXECMEMALLOCHDR_MAGIC 467 && pHdr->pTb != NULL 468 && pHdr->idxChunk == idxChunk) 469 { 470 PIEMTB const pTb = pHdr->pTb; 471 AssertPtr(pTb); 472 Assert((pTb->fFlags & IEMTB_F_TYPE_MASK) == IEMTB_F_TYPE_NATIVE); 473 474 uint32_t const cbBlock = RT_ALIGN_32(pTb->Native.cInstructions * sizeof(IEMNATIVEINSTR) + sizeof(*pHdr), 475 IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SIZE); 476 AssertBreakStmt(offChunk + cbBlock <= cbChunk, offChunk += IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SIZE); /* paranoia */ 477 478 iemTbAllocatorFree(pVCpu, pTb); 479 480 cbPruned += cbBlock; 481 offChunk += cbBlock; 482 } 483 else 484 offChunk += IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SIZE; 485 } 486 STAM_REL_PROFILE_ADD_PERIOD(&pExecMemAllocator->StatPruneRecovered, cbPruned); 487 488 /* 489 * Save the current pruning point. 490 */ 491 pExecMemAllocator->offChunkPrune = offChunk; 492 pExecMemAllocator->idxChunkPrune = idxChunk; 493 494 STAM_REL_PROFILE_STOP(&pExecMemAllocator->StatPruneProf, a); 495 } 496 #endif /* IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING */ 375 497 376 498 … … 558 680 if (iIteration == 0) 559 681 { 682 #ifdef IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING 683 iemExecMemAllocatorPrune(pVCpu, pExecMemAllocator); 684 #else 560 685 /* No header included in the instruction count here. */ 561 686 uint32_t const cNeededInstrs = RT_ALIGN_32(cbReq, IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SIZE) / sizeof(IEMNATIVEINSTR); 562 687 iemTbAllocatorFreeupNativeSpace(pVCpu, cNeededInstrs); 688 #endif 563 689 } 564 690 else … … 1402 1528 * Allocate and initialize the allocatore instance. 1403 1529 */ 1404 size_t cbNeeded = RT_UOFFSETOF_DYN(IEMEXECMEMALLOCATOR, aChunks[cMaxChunks]); 1405 size_t const offBitmaps = RT_ALIGN_Z(cbNeeded, RT_CACHELINE_SIZE); 1406 size_t const cbBitmap = cbChunk >> (IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SHIFT + 3); 1407 cbNeeded += cbBitmap * cMaxChunks; 1530 size_t const offBitmaps = RT_ALIGN_Z(RT_UOFFSETOF_DYN(IEMEXECMEMALLOCATOR, aChunks[cMaxChunks]), RT_CACHELINE_SIZE); 1531 size_t const cbBitmaps = (size_t)(cbChunk >> (IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SHIFT + 3)) * cMaxChunks; 1532 size_t cbNeeded = offBitmaps + cbBitmaps; 1408 1533 AssertCompile(IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SHIFT <= 10); 1409 1534 Assert(cbChunk > RT_BIT_32(IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SHIFT + 3)); … … 1427 1552 pExecMemAllocator->cUnitsPerChunk = cbChunk >> IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SHIFT; 1428 1553 pExecMemAllocator->cBitmapElementsPerChunk = cbChunk >> (IEMEXECMEM_ALT_SUB_ALLOC_UNIT_SHIFT + 6); 1429 memset(pExecMemAllocator->pbmAlloc, 0xff, cbBitmap ); /* Mark everything as allocated. Clear when chunks are added. */1554 memset(pExecMemAllocator->pbmAlloc, 0xff, cbBitmaps); /* Mark everything as allocated. Clear when chunks are added. */ 1430 1555 #if defined(IN_RING3) && !defined(RT_OS_WINDOWS) 1431 1556 pExecMemAllocator->paEhFrames = (PIEMEXECMEMCHUNKEHFRAME)((uintptr_t)pExecMemAllocator + offEhFrames); … … 1476 1601 STAMR3RegisterFU(pUVM, &pExecMemAllocator->StatAlloc, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, 1477 1602 "Profiling the allocator", "/IEM/CPU%u/re/ExecMem/ProfAlloc", pVCpu->idCpu); 1603 #endif 1604 #ifdef IEMEXECMEM_ALT_SUB_WITH_ALT_PRUNING 1605 STAMR3RegisterFU(pUVM, &pExecMemAllocator->StatPruneProf, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, 1606 "Pruning executable memory (alt)", "/IEM/CPU%u/re/ExecMem/Pruning", pVCpu->idCpu); 1607 STAMR3RegisterFU(pUVM, &pExecMemAllocator->StatPruneRecovered, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES_PER_CALL, 1608 "Bytes recovered while pruning", "/IEM/CPU%u/re/ExecMem/PruningRecovered", pVCpu->idCpu); 1478 1609 #endif 1479 1610 -
trunk/src/VBox/VMM/VMMAll/IEMAllThrdRecompiler.cpp
r104064 r104114 114 114 #endif 115 115 116 117 /*********************************************************************************************************************************118 * Internal Functions *119 *********************************************************************************************************************************/120 static void iemTbAllocatorFree(PVMCPUCC pVCpu, PIEMTB pTb);121 116 122 117 … … 921 916 * @thread EMT(pVCpu) 922 917 */ 923 static voidiemTbAllocatorFree(PVMCPUCC pVCpu, PIEMTB pTb)918 DECLHIDDEN(void) iemTbAllocatorFree(PVMCPUCC pVCpu, PIEMTB pTb) 924 919 { 925 920 /* … … 941 936 942 937 /** 943 * Schedules a native TB for freeing when it's not longer being executed and944 * part ofthe caller's call stack.938 * Schedules a TB for freeing when it's not longer being executed and/or part of 939 * the caller's call stack. 945 940 * 946 941 * The TB will be removed from the translation block cache, though, so it isn't … … 964 959 IEMTBALLOC_IDX_MAKE(pTbAllocator, pTb->idxAllocChunk, 965 960 (uintptr_t)(pTb - pTbAllocator->aChunks[pTb->idxAllocChunk].paTbs)))); 966 Assert((pTb->fFlags & IEMTB_F_TYPE_MASK) == IEMTB_F_TYPE_NATIVE); 961 Assert( (pTb->fFlags & IEMTB_F_TYPE_MASK) == IEMTB_F_TYPE_NATIVE 962 || (pTb->fFlags & IEMTB_F_TYPE_MASK) == IEMTB_F_TYPE_THREADED); 967 963 968 964 /* … … 1667 1663 void iemThreadedTbObsolete(PVMCPUCC pVCpu, PIEMTB pTb, bool fSafeToFree) 1668 1664 { 1669 /* Unless it's safe, we can only immediately free threaded TB, as we will 1670 have more code left to execute in native TBs when fSafeToFree == false. */ 1671 if (fSafeToFree || (pTb->fFlags & IEMTB_F_TYPE_THREADED)) 1665 /* We cannot free the current TB (indicated by fSafeToFree) because: 1666 - A threaded TB will have its current call entry accessed 1667 to update pVCpu->iem.s.cInstructions. 1668 - A native TB will have code left to execute. */ 1669 if (fSafeToFree) 1672 1670 iemTbAllocatorFree(pVCpu, pTb); 1673 1671 else -
trunk/src/VBox/VMM/include/IEMInternal.h
r104103 r104114 6071 6071 uint64_t cbInitialExec, uint64_t cbMaxExec, uint32_t cbChunkExec); 6072 6072 void iemThreadedTbObsolete(PVMCPUCC pVCpu, PIEMTB pTb, bool fSafeToFree); 6073 DECLHIDDEN(void) iemTbAllocatorFree(PVMCPUCC pVCpu, PIEMTB pTb); 6073 6074 void iemTbAllocatorProcessDelayedFrees(PVMCPUCC pVCpu, PIEMTBALLOCATOR pTbAllocator); 6074 6075 void iemTbAllocatorFreeupNativeSpace(PVMCPUCC pVCpu, uint32_t cNeededInstrs);
Note:
See TracChangeset
for help on using the changeset viewer.