From 77c3aaa3154016e8ddb2045ccac0a5ec6064d993 Mon Sep 17 00:00:00 2001 From: Aidan Lee Date: Sun, 20 Sep 2026 20:42:52 +0100 Subject: [PATCH 1/7] QuickVec uses size_t --- include/hx/QuickVec.h | 58 ++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/include/hx/QuickVec.h b/include/hx/QuickVec.h index 242790443..7e45374a3 100644 --- a/include/hx/QuickVec.h +++ b/include/hx/QuickVec.h @@ -10,8 +10,8 @@ namespace hx template struct QuickVec { - int mAlloc; - int mSize; + size_t mAlloc; + size_t mSize; T *mPtr; QuickVec() : mPtr(0), mAlloc(0), mSize(0) { } @@ -26,7 +26,7 @@ struct QuickVec if (mSize+1>mAlloc) { mAlloc = 10 + (mSize*3/2); - mPtr = (T *)realloc(mPtr,sizeof(T)*mAlloc); + mPtr = static_cast(realloc(mPtr,sizeof(T)*mAlloc)); } mPtr[mSize]=inT; mSize++; @@ -37,31 +37,31 @@ struct QuickVec std::swap(mSize, inOther.mSize); std::swap(mPtr, inOther.mPtr); } - T *setSize(int inSize) + T *setSize(size_t inSize) { if (inSize>mAlloc) { mAlloc = inSize; - mPtr = (T *)realloc(mPtr,sizeof(T)*mAlloc); + mPtr = static_cast(realloc(mPtr,sizeof(T)*mAlloc)); } mSize = inSize; return mPtr; } // Can push this many without realloc - bool hasExtraCapacity(int inN) + bool hasExtraCapacity(size_t inN) { return mSize+inN<=mAlloc; } - bool safeReserveExtra(int inN) + bool safeReserveExtra(size_t inN) { - int want = mSize + inN; + size_t want{ mSize + inN }; if (want>mAlloc) { - int wantAlloc = 10 + (mSize*3/2); + size_t wantAlloc{ 10 + (mSize * 3 / 2) }; if (wantAlloc(malloc( sizeof(T)*wantAlloc )); if (!newBuffer) return false; mAlloc = wantAlloc; @@ -80,12 +80,12 @@ struct QuickVec { return mPtr[--mSize]; } - inline void qerase(int inPos) + inline void qerase(size_t inPos) { --mSize; mPtr[inPos] = mPtr[mSize]; } - inline void erase(int inPos) + inline void erase(size_t inPos) { --mSize; if (mSize>inPos) @@ -95,31 +95,33 @@ struct QuickVec inline bool qerase_val(T inVal) { - for(int i=0;i=mAlloc) { mAlloc = 10 + (mSize*3/2); - mPtr = (T *)realloc(mPtr,sizeof(T)*mAlloc); + mPtr = static_cast(realloc(mPtr,sizeof(T)*mAlloc)); } return mSize++; } - inline int size() const { return mSize; } - inline T &operator[](int inIndex) { return mPtr[inIndex]; } - inline const T &operator[](int inIndex) const { return mPtr[inIndex]; } + inline size_t size() const { return mSize; } + inline T &operator[](size_t inIndex) { return mPtr[inIndex]; } + inline const T &operator[](size_t inIndex) const { return mPtr[inIndex]; } private: QuickVec(const QuickVec &); @@ -138,8 +140,8 @@ class QuickDeque QuickVec mSpare; QuickVec mActive; - int mHeadPos; - int mTailPos; + size_t mHeadPos; + size_t mTailPos; Slab *mHead; Slab *mTail; @@ -153,9 +155,9 @@ class QuickDeque } ~QuickDeque() { - for(int i=0;i Date: Sun, 20 Sep 2026 20:48:12 +0100 Subject: [PATCH 2/7] hole tracking uses uint8_t MAX_HOLES is 127 --- src/hx/gc/Immix.cpp | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/hx/gc/Immix.cpp b/src/hx/gc/Immix.cpp index f3c7fa67d..7b82e3057 100644 --- a/src/hx/gc/Immix.cpp +++ b/src/hx/gc/Immix.cpp @@ -24,6 +24,24 @@ #include #include +namespace +{ + // It took until C++26 for them to add saturating casts! + // So lets have some very basic saturating maths helpers + + template + T saturating_add(T x, T delta) + { + if (x > std::numeric_limits::max() - delta) + { + return std::numeric_limits::max(); + } + else + { + return x + delta; + } + } +} static bool sgIsCollecting = false; @@ -728,7 +746,7 @@ struct BlockDataInfo unsigned int allocStart[IMMIX_LINES]; HoleRange mRanges[MAX_HOLES]; - int mHoles; + uint8_t mHoles; int mUsedRows; int mMaxHoleSize; @@ -841,7 +859,7 @@ struct BlockDataInfo if (!mReclaimed) reclaim(0); - for(int i=0;iallocStart; // Scan nursery for survivors - for(int hole = 0; holemHoles; hole++) + for (uint8_t hole{ 0 }; hole < from->mHoles; hole++) { int start = from->mRanges[hole].start; int len = from->mRanges[hole].length; @@ -5443,7 +5461,7 @@ class GlobalAllocator } } - int extra = std::max( mAllBlocks.size(), 8<(8 << IMMIX_BLOCK_GROUP_BITS)) }; mFreeBlocks.safeReserveExtra(extra); std::sort(&mFreeBlocks[0], &mFreeBlocks[0] + mFreeBlocks.size(), SmallestFreeFirst ); @@ -5763,8 +5781,8 @@ static int sFragIgnore=0; class LocalAllocator : public hx::StackContext { - int mCurrentHole; - int mCurrentHoles; + uint8_t mCurrentHole; + uint8_t mCurrentHoles; HoleRange *mCurrentRange; int *mFraggedRows; @@ -6308,7 +6326,7 @@ class LocalAllocator : public hx::StackContext spaceStart = mCurrentRange[mCurrentHole].start; spaceEnd = spaceStart + mCurrentRange[mCurrentHole].length; #endif - mCurrentHole++; + mCurrentHole = saturating_add(mCurrentHole, 1); mMoreHoles = mCurrentHole Date: Sun, 20 Sep 2026 21:57:24 +0100 Subject: [PATCH 3/7] Used rows and used bytes use appropriate sized types --- src/hx/gc/Immix.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/hx/gc/Immix.cpp b/src/hx/gc/Immix.cpp index 7b82e3057..226df7121 100644 --- a/src/hx/gc/Immix.cpp +++ b/src/hx/gc/Immix.cpp @@ -26,7 +26,7 @@ namespace { - // It took until C++26 for them to add saturating casts! + // It took until C++26 for them to add saturating arithmetic! // So lets have some very basic saturating maths helpers template @@ -748,10 +748,10 @@ struct BlockDataInfo HoleRange mRanges[MAX_HOLES]; uint8_t mHoles; - int mUsedRows; + uint8_t mUsedRows; int mMaxHoleSize; int mMoveScore; - int mUsedBytes; + uint16_t mUsedBytes; int mFraggedRows; bool mPinned; unsigned char mZeroed; @@ -818,7 +818,7 @@ struct BlockDataInfo void makeFull() { mUsedRows = IMMIX_USEFUL_LINES; - mUsedBytes = mUsedRows<mRowMarked+IMMIX_HEADER_LINES, 1,IMMIX_USEFUL_LINES); mRanges[0].start = 0; @@ -970,7 +970,7 @@ struct BlockDataInfo #endif mUsedRows = (total & 0xff) + ((total>>8) & 0xff) + ((total>>16)&0xff) + ((total>>24)&0xff); - mUsedBytes = mUsedRows<((IMMIX_USEFUL_LINES - mUsedRows) << IMMIX_LINE_BITS) }; if (left Date: Sun, 20 Sep 2026 22:08:10 +0100 Subject: [PATCH 4/7] fragged rows uses uint8_t --- src/hx/gc/Immix.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hx/gc/Immix.cpp b/src/hx/gc/Immix.cpp index 226df7121..d116ac82e 100644 --- a/src/hx/gc/Immix.cpp +++ b/src/hx/gc/Immix.cpp @@ -752,7 +752,7 @@ struct BlockDataInfo int mMaxHoleSize; int mMoveScore; uint16_t mUsedBytes; - int mFraggedRows; + uint8_t mFraggedRows; bool mPinned; unsigned char mZeroed; bool mReclaimed; @@ -5784,7 +5784,7 @@ class LocalAllocator : public hx::StackContext uint8_t mCurrentHole; uint8_t mCurrentHoles; HoleRange *mCurrentRange; - int *mFraggedRows; + uint8_t *mFraggedRows; bool mMoreHoles; @@ -6275,7 +6275,7 @@ class LocalAllocator : public hx::StackContext // spaceOversize might have been set to zero for quick-termination of alloc. unsigned char* s{ spaceOversize }; if (s>spaceFirst && mFraggedRows) - *mFraggedRows += (s - spaceFirst)>>IMMIX_LINE_BITS; + *mFraggedRows += static_cast((s - spaceFirst) >> IMMIX_LINE_BITS); #else #ifdef HXCPP_ALIGN_ALLOC if (!(size_t{ spaceStart } & 0x4)) @@ -6312,7 +6312,7 @@ class LocalAllocator : public hx::StackContext } if (mFraggedRows && spaceEnd > spaceStart) { - *mFraggedRows += static_cast((spaceEnd - spaceStart) >> IMMIX_LINE_BITS); + *mFraggedRows += static_cast((spaceEnd - spaceStart) >> IMMIX_LINE_BITS); } #endif From d6c4193c23b2a5054595cb3a871cf46facd2323d Mon Sep 17 00:00:00 2001 From: Aidan Lee Date: Sun, 20 Sep 2026 22:16:30 +0100 Subject: [PATCH 5/7] size_t for block stat fields --- src/hx/gc/Immix.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hx/gc/Immix.cpp b/src/hx/gc/Immix.cpp index d116ac82e..6ddddb839 100644 --- a/src/hx/gc/Immix.cpp +++ b/src/hx/gc/Immix.cpp @@ -666,12 +666,12 @@ struct BlockDataStats fraggedRows += inOther.fraggedRows; } - int rowsInUse; + size_t rowsInUse; size_t bytesInUse; - int emptyBlocks; - int fragScore; - int fraggedBlocks; - int fraggedRows; + size_t emptyBlocks; + size_t fragScore; + size_t fraggedBlocks; + size_t fraggedRows; }; static BlockDataStats sThreadBlockDataStats[MAX_GC_THREADS]; From 30d24561f077ad6ec04d8c4e08f3babb9c4411ec Mon Sep 17 00:00:00 2001 From: Aidan Lee Date: Mon, 21 Sep 2026 18:28:28 +0100 Subject: [PATCH 6/7] uint16_t for max hole size --- src/hx/gc/Immix.cpp | 66 +++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 41 deletions(-) diff --git a/src/hx/gc/Immix.cpp b/src/hx/gc/Immix.cpp index 6ddddb839..9eb91603e 100644 --- a/src/hx/gc/Immix.cpp +++ b/src/hx/gc/Immix.cpp @@ -27,19 +27,25 @@ namespace { // It took until C++26 for them to add saturating arithmetic! - // So lets have some very basic saturating maths helpers + // So lets have some very basic saturating maths helpers. + // Funky branchless saturating from https://web.archive.org/web/20190213215419/https://locklessinc.com/articles/sat_arithmetic/ template T saturating_add(T x, T delta) { - if (x > std::numeric_limits::max() - delta) - { - return std::numeric_limits::max(); - } - else - { - return x + delta; - } + T res = x + delta; + res |= -(res < x); + + return res; + } + + template + T saturating_sub(T x, T delta) + { + T res = x - delta; + res &= -(res <= x); + + return res; } } @@ -749,7 +755,7 @@ struct BlockDataInfo uint8_t mHoles; uint8_t mUsedRows; - int mMaxHoleSize; + uint16_t mMaxHoleSize; int mMoveScore; uint16_t mUsedBytes; uint8_t mFraggedRows; @@ -1028,7 +1034,7 @@ struct BlockDataInfo { ranges[0].start = IMMIX_HEADER_LINES<(IMMIX_USEFUL_LINES << IMMIX_LINE_BITS); mUsedRows = 0; mHoles = 1; mMoveScore = 0; @@ -1119,15 +1125,15 @@ struct BlockDataInfo mMaxHoleSize = 0; for(int h=0;hstart; - int l = ranges->length; + uint16_t s{ ranges->start }; + uint16_t l{ ranges->length }; freeLines += l; ZERO_MEM(allocStart+s, l*sizeof(int)); - int sBytes = s<(s << IMMIX_LINE_BITS) }; ranges->start = sBytes; - int lBytes = l<(l << IMMIX_LINE_BITS) }; ranges->length = lBytes; if (lBytes>mMaxHoleSize) @@ -1159,9 +1165,9 @@ struct BlockDataInfo mFraggedRows = 0; } - int calcFragScore() + int calcFragScore() const { - return mPinned ? 0 : (mHoles>3 ? mHoles-3 : 0) + 8 * (mUsedRows<(mHoles, 3) + 8 * (mUsedRows<getUsedRows() > inB->getUsedRows(); -} - -bool BiggestFreeFirst(BlockDataInfo *inA, BlockDataInfo *inB) -{ - return inA->mMaxHoleSize > inB->mMaxHoleSize; -} -bool SmallestFreeFirst(BlockDataInfo *inA, BlockDataInfo *inB) +static bool SmallestFreeFirst(BlockDataInfo *inA, BlockDataInfo *inB) { return inA->mMaxHoleSize < inB->mMaxHoleSize; } - -bool LeastUsedFirst(BlockDataInfo *inA, BlockDataInfo *inB) -{ - return inA->getUsedRows() < inB->getUsedRows(); -} - - - -bool SortMoveOrder(BlockDataInfo *inA, BlockDataInfo *inB) +static bool SortMoveOrder(BlockDataInfo *inA, BlockDataInfo *inB) { return inA->mMoveScore > inB->mMoveScore; } - namespace hx { From a54dd3bcd2eb28fe21cd332b433a2452a24e0361 Mon Sep 17 00:00:00 2001 From: Aidan Lee Date: Tue, 22 Sep 2026 20:26:22 +0100 Subject: [PATCH 7/7] Fix indent --- include/hx/QuickVec.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/hx/QuickVec.h b/include/hx/QuickVec.h index 7e45374a3..d8f51c5ca 100644 --- a/include/hx/QuickVec.h +++ b/include/hx/QuickVec.h @@ -97,12 +97,12 @@ struct QuickVec { for (size_t i{ 0 }; i < mSize; i++) { - if (mPtr[i] == inVal) - { - --mSize; - mPtr[i] = mPtr[mSize]; - return true; - } + if (mPtr[i] == inVal) + { + --mSize; + mPtr[i] = mPtr[mSize]; + return true; + } } return false; }