Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 85 additions & 15 deletions src/coreclr/runtime/portable/AllocFast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
//

#include <fcall.h>
#include <gcinterface.h>
#include <vars.hpp>

extern void RhExceptionHandling_FailedAllocation(MethodTable *pMT, bool isOverflow);
EXTERN_C Object* RhpGcAlloc(MethodTable* pMT, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame);

EXTERN_C FCDECL2(Object*, RhpNewVariableSizeObject, MethodTable* pMT, INT_PTR numElements)
static Object* AllocateObject(MethodTable* pMT, uint32_t uFlags, INT_PTR numElements)
{
Object* obj = RhpGcAlloc(pMT, 0, numElements, nullptr);
FCALL_CONTRACT;
Object* obj = RhpGcAlloc(pMT, uFlags, numElements, nullptr);
if (obj == NULL)
{
RhExceptionHandling_FailedAllocation(pMT, false /* isOverflow */);
Expand All @@ -18,11 +21,21 @@ EXTERN_C FCDECL2(Object*, RhpNewVariableSizeObject, MethodTable* pMT, INT_PTR nu
return obj;
}

static Object* _RhpNewArrayFastCore(MethodTable* pMT, INT_PTR size)
EXTERN_C FCDECL2(Object*, RhpNewVariableSizeObject, MethodTable* pMT, INT_PTR numElements)
{
WRAPPER_NO_CONTRACT;
return AllocateObject(pMT, 0, numElements);
}

static Object* NewArrayFastCore(MethodTable* pMT, INT_PTR size)
{
FCALL_CONTRACT;
_ASSERTE(pMT != NULL);
_ASSERTE(size < INT32_MAX);
if (size < 0 || size > INT32_MAX)
{
RhExceptionHandling_FailedAllocation(pMT, true /* isOverflow */);
return nullptr;
}

Thread* thread = GetThread();
ee_alloc_context* cxt = thread->GetEEAllocContext();
Expand All @@ -31,7 +44,7 @@ static Object* _RhpNewArrayFastCore(MethodTable* pMT, INT_PTR size)
sizeInBytes = ALIGN_UP(sizeInBytes, sizeof(void*));

uint8_t* alloc_ptr = cxt->getAllocPtr();
ASSERT(alloc_ptr <= cxt->getAllocLimit());
_ASSERTE(alloc_ptr <= cxt->getAllocLimit());
if ((size_t)(cxt->getAllocLimit() - alloc_ptr) >= sizeInBytes)
{
cxt->setAllocPtr(alloc_ptr + sizeInBytes);
Expand All @@ -41,9 +54,72 @@ static Object* _RhpNewArrayFastCore(MethodTable* pMT, INT_PTR size)
return pObject;
}

return RhpNewVariableSizeObject(pMT, size);
return AllocateObject(pMT, 0, size);
}

#if defined(FEATURE_64BIT_ALIGNMENT)
static Object* NewArrayFastAlign8Core(MethodTable* pMT, INT_PTR size)
{
FCALL_CONTRACT;
_ASSERTE(pMT != NULL);

if (size < 0 || size > INT32_MAX)
{
RhExceptionHandling_FailedAllocation(pMT, true /* isOverflow */);
return nullptr;
}

Thread* thread = GetThread();
ee_alloc_context* cxt = thread->GetEEAllocContext();

size_t sizeInBytes = (size_t)pMT->GetBaseSize() + ((size_t)size * (size_t)pMT->RawGetComponentSize());
sizeInBytes = ALIGN_UP(sizeInBytes, sizeof(void*));

uint8_t* alloc_ptr = cxt->getAllocPtr();
bool requiresAlignObject = !IS_ALIGNED(alloc_ptr, sizeof(int64_t));
size_t paddedSize = sizeInBytes;
if (requiresAlignObject)
{
// We are assuming that allocation of minimal object flips the alignment
paddedSize += MIN_OBJECT_SIZE;
}

_ASSERTE(alloc_ptr <= cxt->getAllocLimit());
if ((size_t)(cxt->getAllocLimit() - alloc_ptr) >= paddedSize)
{
cxt->setAllocPtr(alloc_ptr + paddedSize);
if (requiresAlignObject)
{
Object* dummy = (Object*)alloc_ptr;
dummy->SetMethodTable(g_pFreeObjectMethodTable);
alloc_ptr += MIN_OBJECT_SIZE;
}
PtrArray* pObject = (PtrArray *)alloc_ptr;
pObject->SetMethodTable(pMT);
pObject->SetNumComponents((INT32)size);
return pObject;
}

return AllocateObject(pMT, GC_ALLOC_ALIGN8, size);
}

EXTERN_C FCDECL2(Object*, RhpNewArrayFastAlign8, MethodTable* pMT, INT_PTR size)
{
FCALL_CONTRACT;
_ASSERTE(pMT != NULL);

// if the element count is <= 0x10000, no overflow is possible because the component size is
// <= 0xffff, and thus the product is <= 0xffff0000, and the base size is only ~12 bytes
if (size > 0x10000)
{
// Overflow here should result in an OOM. Let the slow path take care of it.
return AllocateObject(pMT, GC_ALLOC_ALIGN8, size);
}

return NewArrayFastAlign8Core(pMT, size);
}
#endif // FEATURE_64BIT_ALIGNMENT

EXTERN_C FCDECL2(Object*, RhpNewArrayFast, MethodTable* pMT, INT_PTR size)
{
FCALL_CONTRACT;
Expand All @@ -55,11 +131,11 @@ EXTERN_C FCDECL2(Object*, RhpNewArrayFast, MethodTable* pMT, INT_PTR size)
if (size > 0x10000)
{
// Overflow here should result in an OOM. Let the slow path take care of it.
return RhpNewVariableSizeObject(pMT, size);
return AllocateObject(pMT, 0, size);
}
#endif // !HOST_64BIT

return _RhpNewArrayFastCore(pMT, size);
return NewArrayFastCore(pMT, size);
}

EXTERN_C FCDECL2(Object*, RhpNewPtrArrayFast, MethodTable* pMT, INT_PTR size)
Expand All @@ -74,12 +150,6 @@ EXTERN_C FCDECL1(Object*, RhpNewFast, MethodTable* pMT)
return nullptr;
}

EXTERN_C FCDECL2(Object*, RhpNewArrayFastAlign8, MethodTable* pMT, INT_PTR size)
{
PORTABILITY_ASSERT("RhpNewArrayFastAlign8 is not yet implemented");
return nullptr;
}

EXTERN_C FCDECL1(Object*, RhpNewFastAlign8, MethodTable* pMT)
{
PORTABILITY_ASSERT("RhpNewFastAlign8 is not yet implemented");
Expand All @@ -104,5 +174,5 @@ EXTERN_C FCDECL2(Object*, RhNewString, MethodTable* pMT, INT_PTR stringLength)
RhExceptionHandling_FailedAllocation(pMT, false);
}

return _RhpNewArrayFastCore(pMT, stringLength);
return NewArrayFastCore(pMT, stringLength);
}
2 changes: 1 addition & 1 deletion src/coreclr/vm/gchelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ EXTERN_C ee_alloc_context* GetThreadEEAllocContext()
// numElements - number of array elements
// pTransitionBlock- transition frame to make stack crawlable
// Returns a pointer to the object allocated or NULL on failure.
EXTERN_C Object* RhpGcAlloc(MethodTable* pMT, GC_ALLOC_FLAGS uFlags, uintptr_t numElements, TransitionBlock* pTransitionBlock)
EXTERN_C Object* RhpGcAlloc(MethodTable* pMT, GC_ALLOC_FLAGS uFlags, intptr_t numElements, TransitionBlock* pTransitionBlock)
{
OBJECTREF newobj = NULL;

Expand Down
28 changes: 10 additions & 18 deletions src/tests/JIT/interpreter/Interpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2517,16 +2517,12 @@ public static bool TestArray()
if (!ArrayUInt32(32, uint.MinValue)) return false;
if (!ArrayUInt32(32, uint.MaxValue)) return false;

// active issue https://github.com/dotnet/runtime/issues/120659
if (RuntimeInformation.ProcessArchitecture != Architecture.Wasm)
{
// // long
if (!ArrayInt64(0, 0)) return false;
if (!ArrayInt64(1, 1)) return false;
if (!ArrayInt64(32, 32)) return false;
if (!ArrayInt64(32, Int64.MinValue)) return false;
if (!ArrayInt64(32, Int64.MaxValue)) return false;
}
// long
if (!ArrayInt64(0, 0)) return false;
if (!ArrayInt64(1, 1)) return false;
if (!ArrayInt64(32, 32)) return false;
if (!ArrayInt64(32, Int64.MinValue)) return false;
if (!ArrayInt64(32, Int64.MaxValue)) return false;

// float
if (!ArrayFloat(0, 0)) return false;
Expand All @@ -2535,14 +2531,10 @@ public static bool TestArray()
if (!ArrayFloat(32, float.MinValue)) return false;
if (!ArrayFloat(32, float.MaxValue)) return false;

// active issue https://github.com/dotnet/runtime/issues/120659
if (RuntimeInformation.ProcessArchitecture != Architecture.Wasm)
{
// double
if (!ArrayDouble(0, 0)) return false;
if (!ArrayDouble(1, 1)) return false;
if (!ArrayDouble(32, 32)) return false;
}
// double
if (!ArrayDouble(0, 0)) return false;
if (!ArrayDouble(1, 1)) return false;
if (!ArrayDouble(32, 32)) return false;

// ref and value types
if (!TestObjectArray()) return false;
Expand Down