1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +02:00

Improved hotswapping with extension modules

This commit is contained in:
Brian Fiete 2024-12-29 11:02:17 -08:00
parent 769036584a
commit fd4fd43ce3
19 changed files with 836 additions and 232 deletions

View file

@ -2184,6 +2184,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"</Co
<ClInclude Include="util\MappedFile.h" />
<ClInclude Include="util\MathUtils.h" />
<ClInclude Include="util\Matrix4.h" />
<ClInclude Include="util\MultiDictionary.h" />
<ClInclude Include="util\MultiHashSet.h" />
<ClInclude Include="util\PerfTimer.h" />
<ClInclude Include="util\Point.h" />

View file

@ -1132,6 +1132,9 @@
<ClInclude Include="third_party\putty\wildcard.h">
<Filter>src\third_party\putty</Filter>
</ClInclude>
<ClInclude Include="util\MultiDictionary.h">
<Filter>src\util</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="third_party\libffi\i686-pc-cygwin\src\x86\win32.asm">

View file

@ -4,16 +4,16 @@
NS_BF_BEGIN;
template <typename T>
class AllocatorCLib
{
public:
template <typename T>
T* allocate(intptr count)
{
return (T*)malloc(sizeof(T) * count);
}
void deallocate(T* ptr)
void deallocate(void* ptr)
{
free(ptr);
}
@ -27,9 +27,14 @@ public:
{
free(ptr);
}
bool deallocateAll()
{
return false;
}
};
template <typename T, typename TAlloc = AllocatorCLib<T> >
template <typename T, typename TAlloc = AllocatorCLib >
class ArrayBase : public TAlloc
{
public:
@ -445,7 +450,7 @@ protected:
void SetBufferSize(intptr newSize)
{
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (this->mSize > 0)
@ -625,7 +630,7 @@ public:
{
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@ -653,7 +658,7 @@ public:
{
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@ -682,7 +687,7 @@ public:
{
intptr newSize = BF_MAX(this->mSize + count, this->mAllocSize + this->mAllocSize / 2 + 1);
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@ -764,7 +769,7 @@ public:
protected:
void SetBufferSize(intptr newSize)
{
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (this->mSize > 0)
@ -928,7 +933,7 @@ public:
{
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@ -956,7 +961,7 @@ public:
{
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@ -984,7 +989,7 @@ public:
{
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@ -1102,7 +1107,7 @@ public:
}
};
template <typename T, typename TAlloc = AllocatorCLib<T> >
template <typename T, typename TAlloc = AllocatorCLib >
class Array : public ArrayImpl<T, TAlloc, std::is_pod<T>::value>
{
public:

View file

@ -255,23 +255,24 @@ class BumpAllocator : public BumpAllocatorT<0x2000>
};
template <typename T, int ALLOC_SIZE = 0x2000>
class AllocatorBump
template <int ALLOC_SIZE = 0x2000>
class AllocatorBumpT
{
public:
BumpAllocatorT<ALLOC_SIZE>* mAlloc;
AllocatorBump()
AllocatorBumpT()
{
mAlloc = NULL;
}
template <typename T>
T* allocate(intptr count)
{
return (T*)mAlloc->AllocBytes((int)(sizeof(T) * count), alignof(T));
}
void deallocate(T* ptr)
void deallocate(void* ptr)
{
}
@ -286,26 +287,9 @@ public:
}
};
template <int ALLOC_SIZE = 0x2000>
class RawAllocatorBump
class AllocatorBump : public AllocatorBumpT<0x2000>
{
public:
BumpAllocatorT<ALLOC_SIZE>* mAlloc;
RawAllocatorBump()
{
mAlloc = NULL;
}
void* rawAllocate(intptr size)
{
return mAlloc->AllocBytes(size, 16);
}
void rawDeallocate(void* ptr)
{
}
};
NS_BF_END

View file

@ -8,7 +8,7 @@ NS_BF_BEGIN;
#define DEQUE_IDX(i) (this->mVals[((i) + this->mOffset) % this->mAllocSize])
#define DEQUE_IDX_ON(arr, i) ((arr).mVals[((i) + (arr).mOffset) % (arr).mAllocSize])
template <typename T, typename TAlloc = AllocatorCLib<T> >
template <typename T, typename TAlloc = AllocatorCLib >
class DequeBase : public TAlloc
{
public:
@ -370,7 +370,7 @@ protected:
void Grow(intptr newSize)
{
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (this->mSize > 0)
@ -707,7 +707,7 @@ class DequeImpl<T, TAlloc, true> : public DequeBase<T, TAlloc>
protected:
void Grow(intptr newSize)
{
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (this->mSize > 0)
@ -1043,7 +1043,7 @@ public:
}
};
template <typename T, typename TAlloc = AllocatorCLib<T> >
template <typename T, typename TAlloc = AllocatorCLib >
class Deque : public DequeImpl<T, TAlloc, std::is_pod<T>::value>
{
public:

View file

@ -9,7 +9,7 @@ NS_BF_BEGIN;
#ifdef NEW_DICTIONAY
template <typename TKey, typename TValue, typename TAlloc = AllocatorCLib<TKey> >
template <typename TKey, typename TValue, typename TAlloc = AllocatorCLib >
class Dictionary : public TAlloc
{
public:

View file

@ -5,7 +5,7 @@
NS_BF_BEGIN;
template <typename TKey, typename TAlloc = AllocatorCLib<TKey> >
template <typename TKey, typename TAlloc = AllocatorCLib >
class HashSet : public TAlloc
{
public:

View file

@ -0,0 +1,523 @@
#pragma once
#include "../Common.h"
#include "Array.h"
NS_BF_BEGIN;
struct MultiDictionaryFuncs : AllocatorCLib
{
template <typename T>
size_t GetHash(const T& value)
{
return BeefHash<T>()(value);
}
template <typename T>
bool Matches(const T& lhs, const T& rhs)
{
return lhs == rhs;
}
};
template <typename TKey, typename TValue, typename TFuncs = MultiDictionaryFuncs>
class MultiDictionary : public TFuncs
{
public:
struct Entry
{
TKey mKey;
TValue mValue;
int mNext;
int mHashCode;
};
struct EntryRef
{
public:
MultiDictionary* mSet;
int mIndex;
public:
EntryRef()
{
mSet = NULL;
mIndex = -1;
}
EntryRef(MultiDictionary* set, int index)
{
mSet = set;
mIndex = index;
}
Entry* operator*()
{
return &this->mSet->mEntries[this->mIndex];
}
Entry* operator->()
{
return &this->mSet->mEntries[this->mIndex];
}
operator bool() const
{
return this->mIndex != -1;
}
};
struct Iterator
{
public:
MultiDictionary* mSet;
int mCurEntry;
int mCurBucket;
public:
Iterator(MultiDictionary* set)
{
this->mSet = set;
this->mCurBucket = 0;
this->mCurEntry = -1;
}
Iterator& operator++()
{
if (this->mCurEntry != -1)
{
this->mCurEntry = this->mSet->mEntries[this->mCurEntry].mNext;
if (this->mCurEntry != -1)
return *this;
this->mCurBucket++;
}
if (mSet->mHashHeads == NULL)
{
this->mCurBucket = this->mSet->mHashSize;
return *this; // At end
}
while (this->mCurBucket < mSet->mHashSize)
{
this->mCurEntry = this->mSet->mHashHeads[mCurBucket];
if (this->mCurEntry != -1)
return *this;
this->mCurBucket++;
}
return *this; // At end
}
TKey GetKey()
{
return this->mSet->mEntries[this->mCurEntry].mKey;
}
TValue GetValue()
{
return this->mSet->mEntries[this->mCurEntry].mValue;
}
bool operator!=(const Iterator& itr) const
{
return ((itr.mCurEntry != this->mCurEntry) || (itr.mCurBucket != this->mCurBucket));
}
operator bool() const
{
return this->mCurEntry != -1;
}
void MoveToNextHashMatch()
{
int wantHash = this->mSet->mEntries[this->mCurEntry].mHashCode;
do
{
this->mCurEntry = this->mSet->mEntries[this->mCurEntry].mNext;
} while ((this->mCurEntry != -1) && (this->mSet->mEntries[this->mCurEntry].mHashCode != wantHash));
}
};
protected:
int GetPrimeish(int min)
{
// This is a minimal effort to help address-aligned dataa
return (min | 1);
}
int ExpandSize(int oldSize)
{
int newSize = 2 * oldSize;
// Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
// Note that this check works even when mAllocSize overflowed thanks to the (uint) cast
/*if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
{
Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
return MaxPrimeArrayLength;
}*/
return GetPrimeish(newSize);
}
void ResizeEntries()
{
ResizeEntries(ExpandSize(mCount));
}
void ResizeEntries(int newSize)
{
BF_ASSERT(newSize >= mAllocSize);
Entry* newEntries = TFuncs::allocate<Entry>(newSize);
for (int i = 0; i < mCount; i++)
{
auto& newEntry = newEntries[i];
auto& oldEntry = mEntries[i];
newEntry.mHashCode = oldEntry.mHashCode;
newEntry.mNext = oldEntry.mNext;
new (&newEntry.mKey) TKey(std::move(*(TKey*)&oldEntry.mKey));
new (&newEntry.mValue) TValue(std::move(*(TValue*)&oldEntry.mValue));
}
for (int i = mCount; i < newSize; i++)
{
newEntries[i].mHashCode = -1;
}
TFuncs::deallocate(mEntries);
mEntries = newEntries;
mAllocSize = (int)newSize;
}
void FreeIdx(int entryIdx)
{
this->mEntries[entryIdx].mNext = this->mFreeList;
this->mFreeList = entryIdx;
this->mFreeCount++;
}
int AllocEntry()
{
int index;
if (this->mFreeCount > 0)
{
index = this->mFreeList;
this->mFreeList = this->mEntries[index].mNext;
this->mFreeCount--;
}
else
{
if (this->mCount == this->mAllocSize)
ResizeEntries();
index = mCount;
this->mCount++;
}
return index;
}
public:
int* mHashHeads;
int mAllocSize;
Entry* mEntries;
int mFreeList;
int mFreeCount;
static const int cDefaultHashSize = 17;
int mHashSize;
int mCount;
MultiDictionary()
{
this->mHashHeads = NULL;
this->mHashSize = cDefaultHashSize;
this->mEntries = NULL;
this->mAllocSize = 0;
this->mCount = 0;
this->mFreeList = -1;
this->mFreeCount = 0;
}
~MultiDictionary()
{
this->Clear();
}
void EnsureFreeCount(int wantFreeCount)
{
int freeCount = mFreeCount + (mAllocSize - mCount);
if (freeCount >= wantFreeCount)
return;
ResizeEntries(BF_MAX(ExpandSize(mCount), mAllocSize + wantFreeCount - freeCount));
}
int GetCount() const
{
return mCount - mFreeCount;
}
int size() const
{
return mCount - mFreeCount;
}
EntryRef AddRaw(int hash)
{
if (this->mHashHeads == NULL)
{
this->mHashHeads = TFuncs::allocate<int>(mHashSize);
memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
}
int index = AllocEntry();
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
int headEntry = this->mHashHeads[hashIdx];
Entry* newEntry = &mEntries[index];
newEntry->mValue = T();
newEntry->mNext = headEntry;
newEntry->mHashCode = hash;
mHashHeads[hashIdx] = index;
return EntryRef(this, index);
}
void Add(TKey key, TValue value)
{
if (this->mHashHeads == NULL)
{
this->mHashHeads = TFuncs::allocate<int>(mHashSize);
memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
}
int index = AllocEntry();
int hash = TFuncs::GetHash(key);
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
int headEntry = this->mHashHeads[hashIdx];
Entry* newEntry = &mEntries[index];
newEntry->mKey = key;
newEntry->mValue = value;
newEntry->mNext = headEntry;
newEntry->mHashCode = hash;
mHashHeads[hashIdx] = index;
}
void AddAfter(TKey key, TValue value, Entry* afterEntry)
{
int hash = TFuncs::GetHash(key);
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
BF_ASSERT(hash == afterEntry->mHashCode);
int index = AllocEntry();
Entry* newEntry = &mEntries[index];
newEntry->mKey = key;
newEntry->mValue = value;
newEntry->mNext = afterEntry->mNext;
newEntry->mHashCode = hash;
afterEntry->mNext = index;
}
void Rehash(int newHashSize)
{
auto newHashHeads = TFuncs::allocate<int>(newHashSize);
memset(newHashHeads, -1, sizeof(int) * newHashSize);
if (mHashHeads != NULL)
{
SizedArray<int, 1024> entryList;
for (int hashIdx = 0; hashIdx < mHashSize; hashIdx++)
{
int checkEntryIdx = mHashHeads[hashIdx];
if (checkEntryIdx != -1)
{
// We want to keep elements with equal hashes in their insert order so we need to
// iterate through the linked list in reverse
entryList.Clear();
while (checkEntryIdx != -1)
{
entryList.Add(checkEntryIdx);
checkEntryIdx = mEntries[checkEntryIdx].mNext;
}
for (int i = (int)entryList.mSize - 1; i >= 0; i--)
{
int checkEntryIdx = entryList[i];
auto checkEntry = &mEntries[checkEntryIdx];
int newHashIdx = (checkEntry->mHashCode & 0x7FFFFFFF) % newHashSize;
checkEntry->mNext = newHashHeads[newHashIdx];
newHashHeads[newHashIdx] = checkEntryIdx;
}
}
}
TFuncs::deallocate(mHashHeads);
}
mHashHeads = newHashHeads;
mHashSize = newHashSize;
}
void CheckRehash()
{
// Make the lookup load reasonable
if (mHashSize < mCount)
{
this->Rehash(BF_MAX(mCount, (int)(mHashSize * 1.5f)) | 1);
}
}
template <typename TKey>
bool TryGet(const TKey& key, TKey* outKey, TValue* outValue)
{
if (mHashHeads == NULL)
return false;
this->CheckRehash();
int hash = TFuncs::GetHash(key);
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
int checkEntryIdx = this->mHashHeads[hashIdx];
while (checkEntryIdx != -1)
{
Entry* checkEntry = &mEntries[checkEntryIdx];
if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
{
if (outKey != NULL)
*outKey = checkEntry->mKey;
if (outValue != NULL)
*outValue = checkEntry->mValue;
return true;
}
checkEntryIdx = checkEntry->mNext;
}
return false;
}
template <typename TKey>
Iterator TryGet(const TKey& key)
{
if (mHashHeads == NULL)
return end();
this->CheckRehash();
int hash = TFuncs::GetHash(key);
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
int checkEntryIdx = this->mHashHeads[hashIdx];
while (checkEntryIdx != -1)
{
auto checkEntry = &this->mEntries[checkEntryIdx];
if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
{
Iterator itr(this);
itr.mCurEntry = checkEntryIdx;
itr.mCurBucket = hashIdx;
return itr;
}
checkEntryIdx = checkEntry->mNext;
}
return end();
}
template <typename TKey>
bool Remove(const TKey& key)
{
if (mHashHeads == NULL)
return false;
this->CheckRehash();
int hash = TFuncs::GetHash(key);
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
int* srcCheckEntryPtr = &this->mHashHeads[hashIdx];
int checkEntryIdx = *srcCheckEntryPtr;
while (checkEntryIdx != -1)
{
auto checkEntry = &mEntries[checkEntryIdx];
if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
{
*srcCheckEntryPtr = checkEntry->mNext;
FreeIdx(checkEntryIdx);
return true;
}
srcCheckEntryPtr = &checkEntry->mNext;
checkEntryIdx = checkEntry->mNext;
}
return false;
}
Iterator Erase(const Iterator& itr)
{
Iterator next = itr;
++next;
bool found = false;
auto entryIdx = itr.mCurEntry;
auto entry = &mEntries[entryIdx];
int hashIdx = (entry->mHashCode & 0x7FFFFFFF) % this->mHashSize;
int* srcCheckEntryPtr = &this->mHashHeads[hashIdx];
int checkEntryIdx = *srcCheckEntryPtr;
while (checkEntryIdx != -1)
{
auto checkEntry = &mEntries[checkEntryIdx];
if (checkEntryIdx == itr.mCurEntry)
{
*srcCheckEntryPtr = checkEntry->mNext;
found = true;
}
srcCheckEntryPtr = &checkEntry->mNext;
checkEntryIdx = checkEntry->mNext;
}
BF_ASSERT(found);
FreeIdx(entryIdx);
return next;
}
void Clear()
{
if (!TFuncs::deallocateAll())
{
auto itr = begin();
auto endItr = end();
while (itr != endItr)
{
auto entry = itr.mCurEntry;
++itr;
FreeIdx(entry);
}
TFuncs::deallocate(this->mHashHeads);
TFuncs::deallocate(this->mEntries);
}
this->mHashSize = cDefaultHashSize;
this->mHashHeads = NULL;
this->mEntries = NULL;
this->mCount = 0;
}
Iterator begin()
{
return ++Iterator(this);
}
Iterator end()
{
Iterator itr(this);
itr.mCurBucket = this->mHashSize;
return itr;
}
};
NS_BF_END;

View file

@ -30,7 +30,7 @@ struct MultiHashSetFuncs
}
};
template <typename T, typename TFuncs = AllocatorCLib<T> >
template <typename T, typename TFuncs = AllocatorCLib >
class MultiHashSet : public TFuncs
{
public:
@ -180,7 +180,7 @@ protected:
void ResizeEntries(int newSize)
{
BF_ASSERT(newSize >= mAllocSize);
Entry* newEntries = (Entry*)TFuncs::Allocate(sizeof(Entry) * newSize, alignof(Entry));
Entry* newEntries = TFuncs::allocate<Entry>(newSize);
for (int i = 0; i < mCount; i++)
{
@ -195,7 +195,7 @@ protected:
newEntries[i].mHashCode = -1;
}
TFuncs::Deallocate(mEntries);
TFuncs::deallocate(mEntries);
mEntries = newEntries;
mAllocSize = (int)newSize;
@ -276,7 +276,7 @@ public:
{
if (this->mHashHeads == NULL)
{
this->mHashHeads = (int*)TFuncs::Allocate(sizeof(int) * mHashSize, alignof(int));
this->mHashHeads = TFuncs::allocate<int>(mHashSize);
memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
}
@ -298,7 +298,7 @@ public:
{
if (this->mHashHeads == NULL)
{
this->mHashHeads = (int*)TFuncs::Allocate(sizeof(int) * mHashSize, alignof(int));
this->mHashHeads = TFuncs::allocate<int>(mHashSize);
memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
}
@ -332,7 +332,7 @@ public:
void Rehash(int newHashSize)
{
auto newHashHeads = (int*)TFuncs::Allocate(sizeof(int) * newHashSize, alignof(int));
auto newHashHeads = TFuncs::allocate<int>(newHashSize);
memset(newHashHeads, -1, sizeof(int) * newHashSize);
if (mHashHeads != NULL)
@ -364,7 +364,7 @@ public:
}
}
TFuncs::Deallocate(mHashHeads);
TFuncs::deallocate(mHashHeads);
}
mHashHeads = newHashHeads;
mHashSize = newHashSize;
@ -492,7 +492,7 @@ public:
void Clear()
{
if (!TFuncs::DeallocateAll())
if (!TFuncs::deallocateAll())
{
auto itr = begin();
auto endItr = end();
@ -502,8 +502,8 @@ public:
++itr;
FreeIdx(entry);
}
TFuncs::Deallocate(this->mHashHeads);
TFuncs::Deallocate(this->mEntries);
TFuncs::deallocate(this->mHashHeads);
TFuncs::deallocate(this->mEntries);
}
this->mHashSize = cDefaultHashSize;

View file

@ -6,7 +6,7 @@
NS_BF_BEGIN;
template <typename T, typename TAlloc = AllocatorCLib<T> >
template <typename T, typename TAlloc = AllocatorCLib >
class SizedArrayBase : protected TAlloc
{
public:
@ -378,7 +378,7 @@ protected:
void Grow(intptr newSize)
{
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (this->mSize > 0)
@ -533,7 +533,7 @@ public:
{
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@ -562,7 +562,7 @@ public:
{
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@ -638,7 +638,7 @@ public:
protected:
void Grow(intptr newSize)
{
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (this->mSize > 0)
@ -761,7 +761,7 @@ public:
{
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@ -790,7 +790,7 @@ public:
{
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
T* newVals = TAlloc::allocate(newSize);
T* newVals = TAlloc::allocate<T>(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@ -854,14 +854,14 @@ public:
}
};
template <typename T, typename TAlloc = AllocatorCLib<T> >
template <typename T, typename TAlloc = AllocatorCLib >
class SizedArrayImpl : public SizedArrayBaseT<T, TAlloc, std::is_pod<T>::value>
{
public:
typedef SizedArrayBaseT<T, TAlloc, std::is_pod<T>::value> _Base;
};
template <typename T, int TInternalSize, typename TAlloc = AllocatorCLib<T> >
template <typename T, int TInternalSize, typename TAlloc = AllocatorCLib >
class SizedArray : public SizedArrayImpl<T, TAlloc>
{
public:

View file

@ -1063,8 +1063,8 @@ public:
BfIRBlock mIRHeadBlock;
BfIRBlock mIRInitBlock;
BfIRBlock mIREntryBlock;
Array<BfLocalVariable*, AllocatorBump<BfLocalVariable*> > mLocals;
HashSet<BfLocalVarEntry, AllocatorBump<BfLocalVariable*> > mLocalVarSet;
Array<BfLocalVariable*, AllocatorBump> mLocals;
HashSet<BfLocalVarEntry, AllocatorBump> mLocalVarSet;
Array<BfLocalMethod*> mLocalMethods;
Dictionary<String, BfLocalMethod*> mLocalMethodMap;
Dictionary<String, BfLocalMethod*> mLocalMethodCache; // So any lambda 'capturing' and 'processing' stages use the same local method

View file

@ -2690,11 +2690,7 @@ public:
void ReportMemory(MemReporter* memReporter);
};
class BfResolvedTypeSetFuncs : public MultiHashSetFuncs
{
};
class BfResolvedTypeSet : public MultiHashSet<BfType*, BfResolvedTypeSetFuncs>
class BfResolvedTypeSet : public MultiHashSet<BfType*, AllocatorCLib>
{
public:
enum BfHashFlags
@ -2749,7 +2745,7 @@ public:
BfTypeDef* ResolveToTypeDef(BfTypeReference* typeReference, BfType** outType = NULL);
};
class Iterator : public MultiHashSet<BfType*, BfResolvedTypeSetFuncs>::Iterator
class Iterator : public MultiHashSet<BfType*, AllocatorCLib>::Iterator
{
public:
Iterator(MultiHashSet* set) : MultiHashSet::Iterator(set)

View file

@ -1254,7 +1254,7 @@ public:
bool HasCustomAttributes();
};
struct BfTypeDefMapFuncs : public MultiHashSetFuncs
struct BfTypeDefMapFuncs : public AllocatorCLib
{
int GetHash(BfTypeDef* typeDef)
{

View file

@ -21,6 +21,7 @@
#include "Compiler/BfDemangler.h"
#include "BeefySysLib/util/Hash.h"
#include "BeefySysLib/util/BeefPerf.h"
#include "BeefySysLib/util/MultiDictionary.h"
#include "DbgSymSrv.h"
#include "MiniDumpDebugger.h"
@ -5173,7 +5174,6 @@ void DbgModule::ParseHotTargetSections(DataStream* stream, addr_target* resolved
stream->Read(&coffReloc, sizeof(COFFRelocation));
PE_SymInfo* symInfo = (PE_SymInfo*)&mSymbolData[coffReloc.mSymbolTableIndex * 18];
//const char* symName = mSymbolData[coffReloc.mSymbolTableIndex];
bool isStaticSymbol = symInfo->mStorageClass == COFF_SYM_CLASS_STATIC;
@ -5313,27 +5313,20 @@ void DbgModule::CommitHotTargetSections()
}
}
void DbgModule::HotReplaceType(DbgType* newType)
bool DbgModule::HasHotReplacedMethods(DbgType* type)
{
for (auto newMethod : type->mMethodList)
{
if ((newMethod->mBlock.mLowPC >= mImageBase) && (newMethod->mBlock.mHighPC <= mImageBase + mImageSize))
return true;
}
return false;
}
void DbgModule::HotReplaceMethods(DbgType* newType, DbgType* primaryType)
{
auto linkedModule = GetLinkedModule();
newType->PopulateType();
DbgType* primaryType = linkedModule->GetPrimaryType(newType);
if (primaryType == newType)
{
// There was no previous type
BF_ASSERT(primaryType->mHotNewType == NULL);
return;
}
if (primaryType->mHotNewType != newType)
{
// We have already pulled in the new data from a previous new type
BF_ASSERT(primaryType->mHotNewType == NULL);
return;
}
primaryType->mHotNewType = NULL;
primaryType->PopulateType();
linkedModule->ParseGlobalsData();
linkedModule->ParseSymbolData();
@ -5354,13 +5347,15 @@ void DbgModule::HotReplaceType(DbgType* newType)
// Now actually remove the linedata from the defining module
HashSet<DbgSrcFile*> checkedFiles;
for (auto method : primaryType->mMethodList)
//for (auto method : primaryType->mMethodList)
auto _RemoveLineInfo = [&](DbgSubprogram* method)
{
//method->mWasModuleHotReplaced = true;
method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned
if (method->mLineInfo == NULL)
continue;
return;
//FIXME: Hot replacing lines
DbgSrcFile* lastSrcFile = NULL;
@ -5394,51 +5389,52 @@ void DbgModule::HotReplaceType(DbgType* newType)
}
}
}
}
};
//DbgType* primaryType = newType->GetPrimaryType();
MultiDictionary<StringView, DbgSubprogram*> oldProgramMap;
auto _AddToHotReplacedMethodList = [&](DbgSubprogram* oldMethod)
{
int hotIdx = oldMethod->mCompileUnit->mDbgModule->mHotIdx;
if ((hotIdx > 0) && (hotIdx < mDebugTarget->mVDataHotIdx))
{
// Too old
return;
}
oldMethod->PopulateSubprogram();
if (oldMethod->mBlock.IsEmpty())
return;
auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC);
if (symInfo != NULL)
{
oldProgramMap.Add(StringView(symInfo->mName), oldMethod);
}
};
if (newType != primaryType)
{
// We need to keep a persistent list of hot replaced methods so we can set hot jumps
// in old methods that may still be on the callstack. These entries get removed when
// we unload unused hot files in
while (!primaryType->mMethodList.IsEmpty())
{
auto method = primaryType->mMethodList.PopFront();
method->PopulateSubprogram();
primaryType->mHotReplacedMethodList.PushFront(method);
mHotPrimaryTypes.Add(primaryType);
}
}
Dictionary<StringView, DbgSubprogram*> oldProgramMap;
for (auto oldMethod : primaryType->mHotReplacedMethodList)
{
oldMethod->PopulateSubprogram();
if (oldMethod->mBlock.IsEmpty())
continue;
auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC);
if (symInfo != NULL)
{
oldProgramMap.TryAdd(symInfo->mName, oldMethod);
}
_AddToHotReplacedMethodList(oldMethod);
}
bool setHotJumpFailed = false;
while (!newType->mMethodList.IsEmpty())
{
DbgSubprogram* newMethod = newType->mMethodList.PopFront();
if (!newMethod->mBlock.IsEmpty())
{
BfLogDbg("Hot added new method %p %s Address:%p\n", newMethod, newMethod->mName, newMethod->mBlock.mLowPC);
newMethod->PopulateSubprogram();
auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC);
if (symInfo != NULL)
{
DbgSubprogram* oldMethod = NULL;
if (oldProgramMap.TryGetValue(symInfo->mName, &oldMethod))
auto _HotJump = [&](DbgSubprogram* oldMethod, DbgSubprogram* newMethod)
{
bool doHotJump = false;
@ -5520,17 +5516,95 @@ void DbgModule::HotReplaceType(DbgType* newType)
{
if (!setHotJumpFailed)
{
if (!mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC)))
if (mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC)))
{
_RemoveLineInfo(oldMethod);
}
else
setHotJumpFailed = true;
}
oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced;
}
};
auto _ReplaceMethod = [&](DbgSubprogram* newMethod)
{
if (!newMethod->mBlock.IsEmpty())
{
BfLogDbg("Hot added new method %p %s Address:%p\n", newMethod, newMethod->mName, newMethod->mBlock.mLowPC);
newMethod->PopulateSubprogram();
auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC);
if (symInfo != NULL)
{
DbgSubprogram* oldMethod = NULL;
for (auto itr = oldProgramMap.TryGet(StringView(symInfo->mName)); itr != oldProgramMap.end(); ++itr)
{
auto oldMethod = itr.GetValue();
_HotJump(oldMethod, newMethod);
}
}
}
};
if (newType == primaryType)
{
// In-place method swapping
auto newMethod = newType->mMethodList.front();
while (newMethod != NULL)
{
if ((newMethod->mBlock.mLowPC >= mImageBase) && (newMethod->mBlock.mHighPC <= mImageBase + mImageSize))
{
// Our object file contains this function
_ReplaceMethod(newMethod);
auto nextMethod = newMethod->mNext;
newType->mMethodList.Remove(newMethod);
primaryType->mHotReplacedMethodList.PushFront(newMethod);
newMethod = nextMethod;
}
else
{
_AddToHotReplacedMethodList(newMethod);
}
newMethod = newMethod->mNext;
}
}
else
{
while (!newType->mMethodList.IsEmpty())
{
DbgSubprogram* newMethod = newType->mMethodList.PopFront();
_ReplaceMethod(newMethod);
newMethod->mParentType = primaryType;
primaryType->mMethodList.PushBack(newMethod);
}
}
}
void DbgModule::HotReplaceType(DbgType* newType)
{
auto linkedModule = GetLinkedModule();
newType->PopulateType();
DbgType* primaryType = linkedModule->GetPrimaryType(newType);
if (primaryType == newType)
{
// There was no previous type
BF_ASSERT(primaryType->mHotNewType == NULL);
return;
}
if (primaryType->mHotNewType != newType)
{
// We have already pulled in the new data from a previous new type
BF_ASSERT(primaryType->mHotNewType == NULL);
return;
}
primaryType->mHotNewType = NULL;
HotReplaceMethods(newType, primaryType);
//mDebugTarget->mSymbolMap.Get()
@ -6506,6 +6580,10 @@ bool DbgModule::ReadCOFF(DataStream* stream, DbgModuleKind moduleKind)
{
auto dbgType = *itr;
auto primaryType = dbgType->GetPrimaryType();
if ((primaryType->mHotNewType == NULL) && (HasHotReplacedMethods(primaryType)) && (dbgType == primaryType))
HotReplaceMethods(dbgType, primaryType);
if (primaryType != dbgType)
{
mHotPrimaryTypes.Remove(itr);

View file

@ -665,8 +665,8 @@ public:
class SubprogramRecord
{
public:
Array<DbgLineInfoCtx, AllocatorBump<DbgLineInfoCtx> > mContexts;
Array<DbgLineData, AllocatorBump<DbgLineData> > mLines;
Array<DbgLineInfoCtx, AllocatorBump> mContexts;
Array<DbgLineData, AllocatorBump> mLines;
int mCurContext;
bool mHasInlinees;
};
@ -1273,6 +1273,8 @@ public:
void DoReloc(DbgHotTargetSection* hotTargetSection, COFFRelocation& coffReloc, addr_target resolveSymbolAddr, PE_SymInfo* symInfo);
void ParseHotTargetSections(DataStream* stream, addr_target* resovledSymbolAddrs);
void CommitHotTargetSections();
bool HasHotReplacedMethods(DbgType* type);
void HotReplaceMethods(DbgType* newType, DbgType* primaryType);
void HotReplaceType(DbgType* newType);
void ProcessHotSwapVariables();
virtual bool LoadPDB(const String& pdbPath, uint8 wantGuid[16], int32 wantAge) { return false; }

View file

@ -33,6 +33,7 @@ DebugTarget::DebugTarget(WinDebugger* debugger)
mHotHeapAddr = 0;
mHotHeapReserveSize = 0;
mLastHotHeapCleanIdx = 0;
mVDataHotIdx = -1;
mIsEmpty = false;
mWasLocallyBuilt = false;
mCurModuleId = 0;
@ -2489,15 +2490,17 @@ DbgBreakKind DebugTarget::GetDbgBreakKind(addr_target address, CPURegisters* reg
return DbgBreakKind_None;
}
#define ADDR_ROUGH(address) ((address) & ~0x3FFF)
DbgModule* DebugTarget::FindDbgModuleForAddress(addr_target address)
{
addr_target checkAddr = address & ~0xFFFF;
addr_target checkAddr = ADDR_ROUGH(address);
FindDbgModuleCacheEntry* valuePtr = NULL;
if (mFindDbgModuleCache.TryAdd(checkAddr, NULL, &valuePtr))
{
for (auto dwarf : mDbgModules)
{
if ((address >= dwarf->mImageBase) && (address < dwarf->mImageBase + dwarf->mImageSize))
if ((checkAddr >= ADDR_ROUGH(dwarf->mImageBase)) && (checkAddr <= ADDR_ROUGH(dwarf->mImageBase + dwarf->mImageSize)))
{
if (valuePtr->mFirst == NULL)
{

View file

@ -49,6 +49,7 @@ public:
addr_target mHotHeapAddr;
int64 mHotHeapReserveSize;
int mLastHotHeapCleanIdx;
int mVDataHotIdx;
String mTargetPath;
DbgModule* mLaunchBinary;
DbgModule* mTargetBinary;

View file

@ -20,6 +20,6 @@
<LocalDebuggerEnvironment>_NO_DEBUG_HEAP=1</LocalDebuggerEnvironment>
</PropertyGroup>
<PropertyGroup>
<ShowAllFiles>true</ShowAllFiles>
<ShowAllFiles>false</ShowAllFiles>
</PropertyGroup>
</Project>

View file

@ -1151,9 +1151,14 @@ void WinDebugger::HotLoad(const Array<String>& objectFiles, int hotIdx)
int startingModuleIdx = (int)mDebugTarget->mDbgModules.size();
bool hasHotVData = false;
bool failed = false;
for (auto fileName : objectFiles)
{
if ((fileName.IndexOf("/vdata.") != -1) || (fileName.IndexOf("\\vdata.") != -1))
hasHotVData = true;
BfLogDbg("WinDebugger::HotLoad: %s\n", fileName.c_str());
DbgModule* newBinary = mDebugTarget->HotLoad(fileName, hotIdx);
if ((newBinary != NULL) && (newBinary->mFailed))
@ -1186,6 +1191,9 @@ void WinDebugger::HotLoad(const Array<String>& objectFiles, int hotIdx)
mDebugTarget->RehupSrcFiles();
if (hasHotVData)
mDebugTarget->mVDataHotIdx = hotIdx;
for (int breakIdx = 0; breakIdx < (int)mBreakpoints.size(); breakIdx++)
{
auto breakpoint = mBreakpoints[breakIdx];