set(CMAKE_INCLUDE_CURRENT_DIR ON)
set( GCINFO_SOURCES
+ arraylist.cpp
gcinfoencoder.cpp
dbggcinfoencoder.cpp
)
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <stdint.h>
+#include <windows.h>
+#include "debugmacros.h"
+#include "iallocator.h"
+#include "gcinfoarraylist.h"
+#include "safemath.h"
+
+inline size_t roundUp(size_t size, size_t alignment)
+{
+ // `alignment` must be a power of two
+ assert(alignment != 0);
+ assert((alignment & (alignment - 1)) == 0);
+
+ return (size + (alignment - 1)) & ~(alignment - 1);
+}
+
+GcInfoArrayListBase::GcInfoArrayListBase(IAllocator* allocator)
+ : m_allocator(allocator),
+ m_firstChunk(nullptr),
+ m_lastChunk(nullptr),
+ m_lastChunkCount(0),
+ m_lastChunkCapacity(0),
+ m_itemCount(0)
+{
+ assert(m_allocator != nullptr);
+}
+
+GcInfoArrayListBase::~GcInfoArrayListBase()
+{
+ for (ChunkBase* list = m_firstChunk, *chunk; list != nullptr; list = chunk)
+ {
+ chunk = list->m_next;
+ m_allocator->Free(list);
+ }
+}
+
+void GcInfoArrayListBase::AppendNewChunk(size_t firstChunkCapacity, size_t elementSize, size_t chunkAlignment)
+{
+ size_t chunkCapacity = (m_firstChunk == nullptr) ? firstChunkCapacity : (m_lastChunkCapacity * GrowthFactor);
+
+ S_SIZE_T chunkSize = S_SIZE_T(roundUp(sizeof(ChunkBase), chunkAlignment)) + (S_SIZE_T(elementSize) * S_SIZE_T(chunkCapacity));
+ assert(!chunkSize.IsOverflow());
+
+ ChunkBase* chunk = reinterpret_cast<ChunkBase*>(m_allocator->Alloc(chunkSize.Value()));
+ chunk->m_next = nullptr;
+
+ if (m_lastChunk != nullptr)
+ {
+ assert(m_firstChunk != nullptr);
+ m_lastChunk->m_next = chunk;
+ }
+ else
+ {
+ assert(m_lastChunk == nullptr);
+ m_firstChunk = chunk;
+ }
+
+ m_lastChunk = chunk;
+ m_lastChunkCount = 0;
+ m_lastChunkCapacity = chunkCapacity;
+}
+
+GcInfoArrayListBase::IteratorBase::IteratorBase(GcInfoArrayListBase* list, size_t firstChunkCapacity)
+ : m_list(list)
+{
+ assert(m_list != nullptr);
+
+ // Note: if the list is empty, m_list->firstChunk == nullptr == m_list->lastChunk and m_lastChunkCount == 0.
+ // In that case, the next two lines will set m_currentChunk to nullptr and m_currentChunkCount to 0.
+ m_currentChunk = m_list->m_firstChunk;
+ m_currentChunkCount = (m_currentChunk == m_list->m_lastChunk) ? m_list->m_lastChunkCount : firstChunkCapacity;
+}
+
+GcInfoArrayListBase::ChunkBase* GcInfoArrayListBase::IteratorBase::GetNextChunk(size_t& elementCount)
+{
+ if (m_currentChunk == nullptr)
+ {
+ elementCount = 0;
+ return nullptr;
+ }
+
+ ChunkBase* chunk = m_currentChunk;
+ elementCount = m_currentChunkCount;
+
+ m_currentChunk = m_currentChunk->m_next;
+ if (m_currentChunk == nullptr)
+ {
+ m_currentChunkCount = 0;
+ }
+ else if (m_currentChunk == m_list->m_lastChunk)
+ {
+ m_currentChunkCount = m_list->m_lastChunkCount;
+ }
+ else
+ {
+ m_currentChunkCount *= GrowthFactor;
+ }
+
+ return chunk;
+}
#endif
#endif
m_FullyInterruptibleInfoWriter( pJitAllocator ),
- m_LifetimeTransitions()
+ m_LifetimeTransitions( pJitAllocator )
{
_ASSERTE( pCorJitInfo != NULL );
_ASSERTE( pMethodInfo != NULL );
transition.CodeOffset = instructionOffset;
transition.BecomesLive = ( slotState == GC_SLOT_LIVE );
- *( m_LifetimeTransitions.AppendThrowing() ) = transition;
+ *( m_LifetimeTransitions.Append() ) = transition;
}
<!-- Leaf Project Items -->
<ItemGroup>
+ <CppCompile Include="..\ArrayList.cpp" />
<CppCompile Include="..\GCInfoEncoder.cpp" />
<CppCompile Include="..\DbgGCInfoEncoder.cpp" />
</ItemGroup>
)
: m_Info1( pJitAllocator ),
m_Info2( pJitAllocator ),
- m_InterruptibleRanges(),
- m_LifetimeTransitions()
+ m_InterruptibleRanges( pJitAllocator ),
+ m_LifetimeTransitions( pJitAllocator )
#ifdef VERIFY_GCINFO
, m_DbgEncoder(pCorJitInfo, pMethodInfo, pJitAllocator)
#endif
InterruptibleRange range;
range.NormStartOffset = normStartOffset;
range.NormStopOffset = normStopOffset;
- m_pLastInterruptibleRange = m_InterruptibleRanges.AppendThrowing();
+ m_pLastInterruptibleRange = m_InterruptibleRanges.Append();
*m_pLastInterruptibleRange = range;
}
}
transition.BecomesLive = ( slotState == GC_SLOT_LIVE );
transition.IsDeleted = FALSE;
- *( m_LifetimeTransitions.AppendThrowing() ) = transition;
+ *( m_LifetimeTransitions.Append() ) = transition;
LOG((LF_GCINFO, LL_INFO1000000, LOG_GCSLOTDESC_FMT " %s at %x\n", LOG_GCSLOTDESC_ARGS(&m_SlotTable[slotId]), slotState == GC_SLOT_LIVE ? "live" : "dead", instructionOffset));
}
#pragma warning(pop)
#endif
-
-
-//*****************************************************************************
-// StructArrayList is similar to ArrayList, but the element type can be any
-// arbitrary type. Elements can only be accessed sequentially. This is
-// basically just a more efficient linked list - it's useful for accumulating
-// lots of small fixed-size allocations into larger chunks, which would
-// otherwise have an unnecessarily high ratio of heap overhead.
-//
-// The allocator provided must throw an exception on failure.
-//*****************************************************************************
-
-struct StructArrayListEntryBase
-{
- StructArrayListEntryBase *pNext; // actually StructArrayListEntry<ELEMENT_TYPE>*
-};
-
-template<class ELEMENT_TYPE>
-struct StructArrayListEntry : StructArrayListEntryBase
-{
- ELEMENT_TYPE rgItems[1];
-};
-
-class StructArrayListBase
-{
-protected:
-
- typedef void *AllocProc (void *pvContext, SIZE_T cb);
- typedef void FreeProc (void *pvContext, void *pv);
-
- StructArrayListBase ()
- {
- LIMITED_METHOD_CONTRACT;
-
- m_pChunkListHead = NULL;
- m_pChunkListTail = NULL;
- m_nTotalItems = 0;
- }
-
- void Destruct (FreeProc *pfnFree);
-
- void CreateNewChunk (SIZE_T InitialChunkLength, SIZE_T ChunkLengthGrowthFactor, SIZE_T cbElement, AllocProc *pfnAlloc, SIZE_T alignment);
-
- class ArrayIteratorBase
- {
- protected:
-
- void SetCurrentChunk (StructArrayListEntryBase *pChunk, SIZE_T nChunkCapacity);
-
- StructArrayListEntryBase *m_pCurrentChunk;
- SIZE_T m_nItemsInCurrentChunk;
- SIZE_T m_nCurrentChunkCapacity;
- StructArrayListBase *m_pArrayList;
- };
- friend class ArrayIteratorBase;
-
- StructArrayListEntryBase *m_pChunkListHead; // actually StructArrayListEntry<ELEMENT_TYPE>*
- StructArrayListEntryBase *m_pChunkListTail; // actually StructArrayListEntry<ELEMENT_TYPE>*
- SIZE_T m_nItemsInLastChunk;
- SIZE_T m_nTotalItems;
- SIZE_T m_nLastChunkCapacity;
-};
-
-template <class ELEMENT_TYPE,
- SIZE_T INITIAL_CHUNK_LENGTH,
- SIZE_T CHUNK_LENGTH_GROWTH_FACTOR,
- class ALLOCATOR>
-class StructArrayList : public StructArrayListBase
-{
-private:
-
- static_assert_n(1, INITIAL_CHUNK_LENGTH > 0);
- static_assert_n(2, CHUNK_LENGTH_GROWTH_FACTOR > 0);
-
- friend class ArrayIterator;
- friend class ElementIterator;
-
-public:
-
- StructArrayList ()
- {
- LIMITED_METHOD_CONTRACT;
- }
-
- ~StructArrayList ()
- {
- WRAPPER_NO_CONTRACT;
-
- Destruct(&ALLOCATOR::Free);
- }
-
- ELEMENT_TYPE *AppendThrowing ()
- {
- CONTRACTL {
- THROWS;
- } CONTRACTL_END;
-
- if (!m_pChunkListTail || m_nItemsInLastChunk == m_nLastChunkCapacity)
- CreateNewChunk(INITIAL_CHUNK_LENGTH, CHUNK_LENGTH_GROWTH_FACTOR, sizeof(ELEMENT_TYPE), &ALLOCATOR::Alloc, __alignof(ELEMENT_TYPE));
-
- m_nTotalItems++;
- m_nItemsInLastChunk++;
- return &((StructArrayListEntry<ELEMENT_TYPE>*)m_pChunkListTail)->rgItems[m_nItemsInLastChunk-1];
- }
-
- SIZE_T Count ()
- {
- LIMITED_METHOD_CONTRACT;
-
- return m_nTotalItems;
- }
-
- VOID CopyTo (ELEMENT_TYPE *pDest)
- {
- ArrayIterator iter(this);
- ELEMENT_TYPE *pSrc;
- SIZE_T nSrc;
-
- while ((pSrc = iter.GetNext(&nSrc)))
- {
- memcpy(pDest, pSrc, nSrc * sizeof(ELEMENT_TYPE));
- pDest += nSrc;
- }
- }
-
- ELEMENT_TYPE *GetIndex(SIZE_T index)
- {
- ArrayIterator iter(this);
-
- ELEMENT_TYPE * chunk;
- SIZE_T count;
- SIZE_T chunkBaseIndex = 0;
-
- while ((chunk = iter.GetNext(&count)))
- {
- SIZE_T nextBaseIndex = chunkBaseIndex + count;
- if (nextBaseIndex > index)
- {
- return (chunk + (index - chunkBaseIndex));
- }
- chunkBaseIndex = nextBaseIndex;
- }
- // Should never reach here
- assert(false);
- return NULL;
- }
-
- class ArrayIterator : public ArrayIteratorBase
- {
- public:
-
- ArrayIterator (StructArrayList *pArrayList)
- {
- WRAPPER_NO_CONTRACT;
-
- m_pArrayList = pArrayList;
- SetCurrentChunk(pArrayList->m_pChunkListHead, INITIAL_CHUNK_LENGTH);
- }
-
- ELEMENT_TYPE *GetCurrent (SIZE_T *pnElements)
- {
- LIMITED_METHOD_CONTRACT;
-
- ELEMENT_TYPE *pRet = NULL;
- SIZE_T nElements = 0;
-
- if (m_pCurrentChunk)
- {
- pRet = &((StructArrayListEntry<ELEMENT_TYPE>*)m_pCurrentChunk)->rgItems[0];
- nElements = m_nItemsInCurrentChunk;
- }
-
- *pnElements = nElements;
- return pRet;
- }
-
- // Returns NULL when there are no more items.
- ELEMENT_TYPE *GetNext (SIZE_T *pnElements)
- {
- WRAPPER_NO_CONTRACT;
-
- ELEMENT_TYPE *pRet = GetCurrent(pnElements);
-
- if (pRet)
- SetCurrentChunk(m_pCurrentChunk->pNext, m_nCurrentChunkCapacity * CHUNK_LENGTH_GROWTH_FACTOR);
-
- return pRet;
- }
- };
-
- class ItemIterator
- {
-
- typedef StructArrayList<ELEMENT_TYPE, INITIAL_CHUNK_LENGTH, CHUNK_LENGTH_GROWTH_FACTOR, ALLOCATOR>
- SAList;
-
- typedef typename StructArrayList<ELEMENT_TYPE, INITIAL_CHUNK_LENGTH, CHUNK_LENGTH_GROWTH_FACTOR, ALLOCATOR>::ArrayIterator
- ArrIter;
-
- public:
-
- ItemIterator(SAList *list) : iter(list)
- {
- currentChunkIndex = 0;
- currentChunk = iter.GetNext(¤tChunkCount);
- }
-
- ELEMENT_TYPE *
- GetNext()
- {
- ELEMENT_TYPE * returnElem = (currentChunk + currentChunkIndex);
-
- currentChunkIndex++;
- if (currentChunkIndex == currentChunkCount)
- {
- currentChunkIndex = 0;
- currentChunk = iter.GetNext(¤tChunkCount);
- }
- return returnElem;
- }
-
- ELEMENT_TYPE *operator*()
- {
- return currentChunk + currentChunkIndex;
- }
-
- void operator++(int dummy) //int dummy is c++ for "this is postfix ++"
- {
- GetNext();
- }
-
- void operator++() // prefix ++
- {
- GetNext();
- }
-
- void MakeEnd()
- {
- currentChunk = NULL;
- currentChunkIndex = 0;
- }
-
- bool operator!=(const ItemIterator &other)
- {
- if (currentChunk != other.currentChunk)
- return true;
- if (currentChunkIndex != other.currentChunkIndex)
- return true;
-
- return false;
- }
-
- operator bool()
- {
- return currentChunk != NULL;
- }
-
- private:
- unsigned currentChunkIndex;
- SIZE_T currentChunkCount;
- ELEMENT_TYPE *currentChunk;
- ArrIter iter;
- };
-
-
- ItemIterator begin()
- {
- return ItemIterator(this);
- }
-
- ItemIterator end()
- {
- ItemIterator result = ItemIterator(this);
- result.MakeEnd();
- return result;
- }
-};
-
-
#endif
#include "utilcode.h"
#include "corjit.h"
#include "list.h" // for SList
-#include "arraylist.h"
+#include "iallocator.h"
+#include "gcinfoarraylist.h"
#include "stdmacros.h"
#include "gcinfotypes.h"
bool BecomesLive;
};
- class LifetimeTransitionAllocator
- {
- public:
-
- static void *Alloc (void *context, SIZE_T cb);
- static void Free (void *context, void *pv);
- };
-
ICorJitInfo* m_pCorJitInfo;
CORINFO_METHOD_INFO* m_pMethodInfo;
IJitAllocator* m_pAllocator;
#endif
BitStreamWriter m_FullyInterruptibleInfoWriter;
- StructArrayList<LifetimeTransition, 64, 2, LifetimeTransitionAllocator> m_LifetimeTransitions;
+ GcInfoArrayList<LifetimeTransition, 64> m_LifetimeTransitions;
LifetimeTransition *m_rgSortedTransitions;
bool m_IsVarArg;
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef _GCINFOARRAYLIST_H_
+#define _GCINFOARRAYLIST_H_
+
+// GCInfoArrayList is basically a more efficient linked list--it's useful for accumulating
+// lots of small fixed-size allocations into larger chunks which in a typical linked list
+// would incur an unnecessarily high amount of overhead.
+
+class GcInfoArrayListBase
+{
+private:
+ static const size_t GrowthFactor = 2;
+
+protected:
+ friend class IteratorBase;
+
+ struct ChunkBase
+ {
+ ChunkBase* m_next; // actually GcInfoArrayListChunk<ElementType>*
+ };
+
+ class IteratorBase
+ {
+ protected:
+ IteratorBase(GcInfoArrayListBase* list, size_t firstChunkCapacity);
+ ChunkBase* GetNextChunk(size_t& elementCount);
+
+ private:
+ GcInfoArrayListBase* m_list;
+ ChunkBase* m_currentChunk;
+ size_t m_currentChunkCount;
+ };
+
+ GcInfoArrayListBase(IAllocator* allocator);
+ virtual ~GcInfoArrayListBase();
+
+ void AppendNewChunk(size_t firstChunkCapacity, size_t elementSize, size_t chunkAlignment);
+
+public:
+ size_t Count()
+ {
+ return m_itemCount;
+ }
+
+protected:
+ IAllocator* m_allocator;
+ ChunkBase* m_firstChunk; // actually GcInfoArrayListChunk<ElementType>*
+ ChunkBase* m_lastChunk; // actually GcInfoArrayListChunk<ElementType>*
+ size_t m_lastChunkCount;
+ size_t m_lastChunkCapacity;
+ size_t m_itemCount;
+};
+
+template <typename ElementType, size_t FirstChunkCapacity>
+class GcInfoArrayList : public GcInfoArrayListBase
+{
+private:
+ struct Chunk : public ChunkBase
+ {
+ ElementType m_items[];
+ };
+
+public:
+ friend class Iterator;
+
+ struct Iterator : IteratorBase
+ {
+ Iterator(GcInfoArrayList* list)
+ : IteratorBase(list, FirstChunkCapacity)
+ {
+ }
+
+ ElementType* GetNext(size_t* elementCount)
+ {
+ Chunk* chunk = reinterpret_cast<Chunk*>(GetNextChunk(*elementCount));
+ return chunk == nullptr ? nullptr : &chunk->m_items[0];
+ }
+ };
+
+ GcInfoArrayList(IAllocator* allocator)
+ : GcInfoArrayListBase(allocator)
+ {
+ }
+
+ ElementType* Append()
+ {
+ if (m_lastChunk == nullptr || m_lastChunkCount == m_lastChunkCapacity)
+ {
+ AppendNewChunk(FirstChunkCapacity, sizeof(ElementType), __alignof(ElementType));
+ }
+
+ m_itemCount++;
+ m_lastChunkCount++;
+ return &reinterpret_cast<Chunk*>(m_lastChunk)->m_items[m_lastChunkCount - 1];
+ }
+
+ void CopyTo(ElementType* dest)
+ {
+ Iterator iter(this);
+ ElementType* source;
+ size_t elementCount;
+
+ while (source = iter.GetNext(&elementCount), source != nullptr)
+ {
+ memcpy(dest, source, elementCount * sizeof(ElementType));
+ dest += elementCount;
+ }
+ }
+};
+
+#endif
#include "utilcode.h"
#include "corjit.h"
#include "slist.h" // for SList
-#include "arraylist.h"
#include "iallocator.h"
+#include "gcinfoarraylist.h"
#include "stdmacros.h"
#include "eexcp.h"
#endif
UINT32 NormStopOffset;
};
- class InterruptibleRangeAllocator
- {
- public:
-
- static void *Alloc (void *context, SIZE_T cb)
- {
- GcInfoEncoder *pGcInfoEncoder = CONTAINING_RECORD(context, GcInfoEncoder, m_InterruptibleRanges);
- return pGcInfoEncoder->m_pAllocator->Alloc(cb);
- }
-
- static void Free (void *context, void *pv)
- {
- #ifdef MUST_CALL_IALLOCATOR_FREE
- GcInfoEncoder *pGcInfoEncoder = CONTAINING_RECORD(context, GcInfoEncoder, m_InterruptibleRanges);
- pGcInfoEncoder->m_pAllocator->Free(pv);
- #endif
- }
- };
-
- class LifetimeTransitionAllocator
- {
- public:
-
- static void *Alloc (void *context, SIZE_T cb)
- {
- GcInfoEncoder *pGcInfoEncoder = CONTAINING_RECORD(context, GcInfoEncoder, m_LifetimeTransitions);
- return pGcInfoEncoder->m_pAllocator->Alloc(cb);
- }
-
- static void Free (void *context, void *pv)
- {
- #ifdef MUST_CALL_IALLOCATOR_FREE
- GcInfoEncoder *pGcInfoEncoder = CONTAINING_RECORD(context, GcInfoEncoder, m_LifetimeTransitions);
- pGcInfoEncoder->m_pAllocator->Free(pv);
- #endif
- }
- };
-
ICorJitInfo* m_pCorJitInfo;
CORINFO_METHOD_INFO* m_pMethodInfo;
IAllocator* m_pAllocator;
BitStreamWriter m_Info1; // Used for everything except for chunk encodings
BitStreamWriter m_Info2; // Used for chunk encodings
- StructArrayList<InterruptibleRange, 8, 2, InterruptibleRangeAllocator> m_InterruptibleRanges;
- StructArrayList<LifetimeTransition, 64, 2, LifetimeTransitionAllocator> m_LifetimeTransitions;
+ GcInfoArrayList<InterruptibleRange, 8> m_InterruptibleRanges;
+ GcInfoArrayList<LifetimeTransition, 64> m_LifetimeTransitions;
bool m_IsVarArg;
bool m_WantsReportOnlyLeaf;
}
#endif // #ifdef DACCESS_COMPILE
-
-
-void StructArrayListBase::Destruct (FreeProc *pfnFree)
-{
- WRAPPER_NO_CONTRACT;
-
- StructArrayListEntryBase *pList = m_pChunkListHead;
- while (pList)
- {
- StructArrayListEntryBase *pTrash = pList;
- pList = pList->pNext;
- pfnFree(this, pTrash);
- }
-}
-
-// Copied from jit.h
-inline
-size_t roundUp(size_t size, size_t mult = sizeof(size_t))
-{
- assert(mult && ((mult & (mult-1)) == 0)); // power of two test
-
- return (size + (mult - 1)) & ~(mult - 1);
-}
-
-void StructArrayListBase::CreateNewChunk (SIZE_T InitialChunkLength, SIZE_T ChunkLengthGrowthFactor, SIZE_T cbElement, AllocProc *pfnAlloc, SIZE_T alignment)
-{
- CONTRACTL {
- THROWS;
- PRECONDITION(!m_pChunkListHead || m_nItemsInLastChunk == m_nLastChunkCapacity);
- } CONTRACTL_END;
-
- SIZE_T nChunkCapacity;
- if (!m_pChunkListHead)
- nChunkCapacity = InitialChunkLength;
- else
- nChunkCapacity = m_nLastChunkCapacity * ChunkLengthGrowthFactor;
-
- S_SIZE_T cbBaseSize = S_SIZE_T(roundUp(sizeof(StructArrayListEntryBase), alignment));
- S_SIZE_T cbChunk = cbBaseSize +
- S_SIZE_T(cbElement) * S_SIZE_T(nChunkCapacity);
- _ASSERTE(!cbChunk.IsOverflow());
- if(cbChunk.IsOverflow())
- {
- ThrowWin32(ERROR_ARITHMETIC_OVERFLOW);
- }
-
- StructArrayListEntryBase *pNewChunk = (StructArrayListEntryBase*)pfnAlloc(this, cbChunk.Value());
-
- if (m_pChunkListTail)
- {
- _ASSERTE(m_pChunkListHead);
- m_pChunkListTail->pNext = pNewChunk;
- }
- else
- {
- _ASSERTE(!m_pChunkListHead);
- m_pChunkListHead = pNewChunk;
- }
-
- pNewChunk->pNext = NULL;
- m_pChunkListTail = pNewChunk;
-
- m_nItemsInLastChunk = 0;
- m_nLastChunkCapacity = nChunkCapacity;
-}
-
-
-void StructArrayListBase::ArrayIteratorBase::SetCurrentChunk (StructArrayListEntryBase *pChunk, SIZE_T nChunkCapacity)
-{
- LIMITED_METHOD_CONTRACT;
-
- m_pCurrentChunk = pChunk;
-
- if (pChunk)
- {
- if (pChunk == m_pArrayList->m_pChunkListTail)
- m_nItemsInCurrentChunk = m_pArrayList->m_nItemsInLastChunk;
- else
- m_nItemsInCurrentChunk = nChunkCapacity;
-
- m_nCurrentChunkCapacity = nChunkCapacity;
- }
-}
-