Speed up string allocations by 35%
`FastAllocateString` (the choke point through which all string allocations go through) wasn't as fast as it could be and we were 30% slower than CLR on allocating strings. We were leaving a lot of perf on the table. Before this change, string allocation was using the same allocator as arrays. Since there's a subtle difference between the failure modes on overflow (string allocation throws OOM, array allocation throws OverflowException), `FastAllocateString` required a try/catch block to handle the corner case. This was inhibiting codegen optimizations around this code path - to fix that problem, we needed a separate allocator. And since we now had a separate allocator for strings, I also took the liberty of inlining some details around strings (component size and base size) into the helper. It turns out runtime already hardcodes the details around strings (the component size) in a couple places anyway, so this is not that big of a "separation of concerns" violation as it looks like. [tfs-changeset: 1670224]
This commit is contained in:
Родитель
d714e3be0b
Коммит
1e9054a911
|
@ -538,7 +538,7 @@ namespace ILCompiler.DependencyAnalysis
|
|||
// SyncBlock + EETypePtr + length + firstChar
|
||||
objectSize = 2 * pointerSize +
|
||||
sizeof(int) +
|
||||
sizeof(char);
|
||||
StringComponentSize.Value;
|
||||
}
|
||||
|
||||
objData.EmitInt(objectSize);
|
||||
|
|
|
@ -30,6 +30,11 @@ ASM_OFFSET( 0, 0, Object, m_pEEType)
|
|||
|
||||
ASM_OFFSET( 4, 8, Array, m_Length)
|
||||
|
||||
ASM_OFFSET( 4, 8, String, m_Length)
|
||||
ASM_OFFSET( 8, C, String, m_FirstChar)
|
||||
ASM_CONST( 2, 2, STRING_COMPONENT_SIZE)
|
||||
ASM_CONST( E, 16, STRING_BASE_SIZE)
|
||||
|
||||
ASM_OFFSET( 0, 0, EEType, m_usComponentSize)
|
||||
ASM_OFFSET( 2, 2, EEType, m_usFlags)
|
||||
ASM_OFFSET( 4, 4, EEType, m_uBaseSize)
|
||||
|
|
|
@ -25,6 +25,10 @@ class AsmOffsets
|
|||
{
|
||||
static_assert(sizeof(Thread::m_rgbAllocContextBuffer) >= sizeof(gc_alloc_context), "Thread::m_rgbAllocContextBuffer is not big enough to hold a gc_alloc_context");
|
||||
|
||||
// Some assembly helpers for arrays and strings are shared and use the fact that arrays and strings have similar layouts)
|
||||
static_assert(offsetof(Array, m_Length) == offsetof(String, m_Length), "The length field of String and Array have different offsets");
|
||||
static_assert(sizeof(((Array*)0)->m_Length) == sizeof(((String*)0)->m_Length), "The length field of String and Array have different sizes");
|
||||
|
||||
#define PLAT_ASM_OFFSET(offset, cls, member) \
|
||||
static_assert((offsetof(cls, member) == 0x##offset) || (offsetof(cls, member) > 0x##offset), "Bad asm offset for '" #cls "." #member "', the actual offset is smaller than 0x" #offset "."); \
|
||||
static_assert((offsetof(cls, member) == 0x##offset) || (offsetof(cls, member) < 0x##offset), "Bad asm offset for '" #cls "." #member "', the actual offset is larger than 0x" #offset ".");
|
||||
|
|
|
@ -100,3 +100,28 @@ public:
|
|||
void* GetArrayData();
|
||||
};
|
||||
typedef DPTR(Array) PTR_Array;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
class String : public Object
|
||||
{
|
||||
friend class AsmOffsets;
|
||||
friend class StringConstants;
|
||||
|
||||
UInt32 m_Length;
|
||||
UInt16 m_FirstChar;
|
||||
};
|
||||
typedef DPTR(String) PTR_String;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
class StringConstants
|
||||
{
|
||||
public:
|
||||
static UIntNative const ComponentSize = sizeof(((String*)0)->m_FirstChar);
|
||||
static UIntNative const BaseSize = sizeof(ObjHeader) + offsetof(String, m_FirstChar) + ComponentSize;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static UIntNative const STRING_COMPONENT_SIZE = StringConstants::ComponentSize;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static UIntNative const STRING_BASE_SIZE = StringConstants::BaseSize;
|
||||
|
|
|
@ -119,6 +119,85 @@ LOCAL_LABEL(NewOutOfMemory):
|
|||
NESTED_END RhpNewObject, _TEXT
|
||||
|
||||
|
||||
// Allocate a string.
|
||||
// RDI == EEType
|
||||
// ESI == character/element count
|
||||
NESTED_ENTRY RhpNewString, _TEXT, NoHandler
|
||||
// we want to limit the element count to the non-negative 32-bit int range
|
||||
cmp rsi, 07fffffffh
|
||||
ja LOCAL_LABEL(StringSizeOverflow)
|
||||
|
||||
push_nonvol_reg rbx
|
||||
push_nonvol_reg r12
|
||||
push_register rcx // padding
|
||||
|
||||
mov rbx, rdi // save EEType
|
||||
mov r12, rsi // save element count
|
||||
|
||||
// rax = GetThread()
|
||||
INLINE_GETTHREAD
|
||||
|
||||
mov rcx, rax // rcx = Thread*
|
||||
|
||||
// Compute overall allocation size (align(base size + (element size * elements), 8)).
|
||||
lea rax, [(r12 * STRING_COMPONENT_SIZE) + (STRING_BASE_SIZE + 7)]
|
||||
and rax, -8
|
||||
|
||||
// rax == string size
|
||||
// rbx == EEType
|
||||
// rcx == Thread*
|
||||
// r12 == element count
|
||||
|
||||
mov rdx, rax
|
||||
add rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr]
|
||||
jc LOCAL_LABEL(RhpNewString_RarePath)
|
||||
|
||||
// rax == new alloc ptr
|
||||
// rbx == EEType
|
||||
// rcx == Thread*
|
||||
// rdx == string size
|
||||
// r12 == element count
|
||||
cmp rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_limit]
|
||||
ja LOCAL_LABEL(RhpNewString_RarePath)
|
||||
|
||||
mov [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax
|
||||
|
||||
// calc the new object pointer
|
||||
sub rax, rdx
|
||||
|
||||
mov [rax + OFFSETOF__Object__m_pEEType], rbx
|
||||
mov [rax + OFFSETOF__String__m_Length], r12d
|
||||
|
||||
.cfi_remember_state
|
||||
pop_register rcx // padding
|
||||
pop_nonvol_reg r12
|
||||
pop_nonvol_reg rbx
|
||||
ret
|
||||
|
||||
.cfi_restore_state
|
||||
.cfi_def_cfa_offset 32 // workaround cfi_restore_state bug
|
||||
LOCAL_LABEL(RhpNewString_RarePath):
|
||||
mov rdi, rbx // restore EEType
|
||||
mov rsi, r12 // restore element count
|
||||
// passing string size in rdx
|
||||
|
||||
pop_register rcx // padding
|
||||
pop_nonvol_reg r12
|
||||
pop_nonvol_reg rbx
|
||||
jmp C_FUNC(RhpNewArrayRare)
|
||||
|
||||
LOCAL_LABEL(StringSizeOverflow):
|
||||
// We get here if the size of the final string object can't be represented as an unsigned
|
||||
// 32-bit value. We're going to tail-call to a managed helper that will throw
|
||||
// an OOM exception that the caller of this allocator understands.
|
||||
|
||||
// rdi holds EEType pointer already
|
||||
xor esi, esi // Indicate that we should throw OOM.
|
||||
jmp C_FUNC(RhExceptionHandling_FailedAllocation)
|
||||
|
||||
NESTED_END RhpNewString, _TEXT
|
||||
|
||||
|
||||
// Allocate one dimensional, zero based array (SZARRAY).
|
||||
// RDI == EEType
|
||||
// ESI == element count
|
||||
|
|
|
@ -102,6 +102,58 @@ NewOutOfMemory:
|
|||
NESTED_END RhpNewObject, _TEXT
|
||||
|
||||
|
||||
;; Allocate a string.
|
||||
;; RCX == EEType
|
||||
;; EDX == character/element count
|
||||
LEAF_ENTRY RhNewString, _TEXT
|
||||
|
||||
; we want to limit the element count to the non-negative 32-bit int range
|
||||
cmp rdx, 07fffffffh
|
||||
ja StringSizeOverflow
|
||||
|
||||
; Compute overall allocation size (align(base size + (element size * elements), 8)).
|
||||
lea rax, [(rdx * STRING_COMPONENT_SIZE) + (STRING_BASE_SIZE + 7)]
|
||||
and rax, -8
|
||||
|
||||
; rax == string size
|
||||
; rcx == EEType
|
||||
; rdx == element count
|
||||
|
||||
INLINE_GETTHREAD r10, r8
|
||||
|
||||
mov r8, rax
|
||||
add rax, [r10 + OFFSETOF__Thread__m_alloc_context__alloc_ptr]
|
||||
jc RhpNewArrayRare
|
||||
|
||||
; rax == new alloc ptr
|
||||
; rcx == EEType
|
||||
; rdx == element count
|
||||
; r8 == array size
|
||||
; r10 == thread
|
||||
cmp rax, [r10 + OFFSETOF__Thread__m_alloc_context__alloc_limit]
|
||||
ja RhpNewArrayRare
|
||||
|
||||
mov [r10 + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax
|
||||
|
||||
; calc the new object pointer
|
||||
sub rax, r8
|
||||
|
||||
mov [rax + OFFSETOF__Object__m_pEEType], rcx
|
||||
mov [rax + OFFSETOF__String__m_Length], edx
|
||||
|
||||
ret
|
||||
|
||||
StringSizeOverflow:
|
||||
; We get here if the size of the final string object can't be represented as an unsigned
|
||||
; 32-bit value. We're going to tail-call to a managed helper that will throw
|
||||
; an OOM exception that the caller of this allocator understands.
|
||||
|
||||
; rcx holds EEType pointer already
|
||||
xor edx, edx ; Indicate that we should throw OOM.
|
||||
jmp RhExceptionHandling_FailedAllocation
|
||||
LEAF_END RhNewString, _TEXT
|
||||
|
||||
|
||||
;; Allocate one dimensional, zero based array (SZARRAY).
|
||||
;; RCX == EEType
|
||||
;; EDX == element count
|
||||
|
|
|
@ -112,24 +112,97 @@ LOCAL_LABEL(NewOutOfMemory):
|
|||
|
||||
NESTED_END RhpNewObject, _TEXT
|
||||
|
||||
|
||||
// Allocate a string.
|
||||
// r0 == EEType
|
||||
// r1 == element/character count
|
||||
LEAF_ENTRY RhpNewString, _TEXT
|
||||
PROLOG_PUSH "{r4-r6,lr}"
|
||||
// Make sure computing the overall allocation size won't overflow
|
||||
MOV32 r12, ((0xFFFFFFFF - STRING_BASE_SIZE - 3) / STRING_COMPONENT_SIZE)
|
||||
cmp r1, r12
|
||||
bhi LOCAL_LABEL(StringSizeOverflow)
|
||||
|
||||
// Compute overall allocation size (align(base size + (element size * elements), 4)).
|
||||
mov r2, #(STRING_BASE_SIZE + 3)
|
||||
#if STRING_COMPONENT_SIZE == 2
|
||||
add r2, r2, r1, lsl #1 // r2 += characters * 2
|
||||
#else
|
||||
NotImplementedComponentSize
|
||||
#endif
|
||||
bic r2, r2, #3
|
||||
|
||||
mov r4, r0 // Save EEType
|
||||
mov r5, r1 // Save element count
|
||||
mov r6, r2 // Save string size
|
||||
// r0 = GetThread()
|
||||
INLINE_GETTHREAD
|
||||
// r4 == EEType
|
||||
// r5 == element count
|
||||
// r6 == string size
|
||||
// r0 == Thread*
|
||||
|
||||
// Load potential new object address into r12.
|
||||
ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr]
|
||||
|
||||
// Determine whether the end of the object would lie outside of the current allocation context. If so,
|
||||
// we abandon the attempt to allocate the object directly and fall back to the slow helper.
|
||||
adds r6, r12
|
||||
bcs LOCAL_LABEL(RhpNewString_RarePath) // if we get a carry here, the string is too large to fit below 4 GB
|
||||
|
||||
ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_limit]
|
||||
cmp r6, r12
|
||||
bhi LOCAL_LABEL(RhpNewString_RarePath)
|
||||
|
||||
// Reload new object address into r12.
|
||||
ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr]
|
||||
|
||||
// Update the alloc pointer to account for the allocation.
|
||||
str r6, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr]
|
||||
|
||||
// Set the new object's EEType pointer and element count.
|
||||
str r4, [r12, #OFFSETOF__Object__m_pEEType]
|
||||
str r5, [r12, #OFFSETOF__String__m_Length]
|
||||
|
||||
// Return the object allocated in r0.
|
||||
mov r0, r12
|
||||
EPILOG_POP "{r4-r6,pc}"
|
||||
|
||||
LOCAL_LABEL(StringSizeOverflow):
|
||||
// We get here if the size of the final string object can't be represented as an unsigned
|
||||
// 32-bit value. We're going to tail-call to a managed helper that will throw
|
||||
// an OOM exception that the caller of this allocator understands.
|
||||
|
||||
// EEType is in r0 already
|
||||
mov r1, 0 // Indicate that we should throw OOM
|
||||
EPILOG_POP "{r4-r6,lr}"
|
||||
b C_FUNC(RhExceptionHandling_FailedAllocation)
|
||||
|
||||
LOCAL_LABEL(RhpNewString_RarePath):
|
||||
mov r3, r0
|
||||
mov r0, r4
|
||||
mov r1, r5
|
||||
mov r2, r6
|
||||
// r0 == EEType
|
||||
// r1 == element count
|
||||
// r2 == string size + Thread::m_alloc_context::alloc_ptr
|
||||
// r3 == Thread
|
||||
EPILOG_POP "{r4-r6,lr}"
|
||||
b C_FUNC(RhpNewArrayRare)
|
||||
|
||||
LEAF_END RhpNewString, _TEXT
|
||||
|
||||
|
||||
// Allocate one dimensional, zero based array (SZARRAY).
|
||||
// r0 == EEType
|
||||
// r1 == element count
|
||||
LEAF_ENTRY RhpNewArray, _TEXT
|
||||
PROLOG_PUSH "{r4-r6,lr}"
|
||||
// we want to limit the element count to the non-negative 32-bit int range
|
||||
movw r12, #0xffff
|
||||
movt r12, #0x7fff
|
||||
cmp r1, r12
|
||||
bhi LOCAL_LABEL(ArraySizeOverflow)
|
||||
// if the element count is negative, it's an overflow error
|
||||
cmp r1, #0
|
||||
blt LOCAL_LABEL(ArraySizeOverflow)
|
||||
|
||||
// Compute overall allocation size (align(base size + (element size * elements), 4)).
|
||||
// if the element count is <= 0x10000, no overflow is possible because the component
|
||||
// size is <= 0xffff (it's an unsigned 16-bit value) and thus the product is <= 0xffff0000
|
||||
// and the base size is only 12 bytes.
|
||||
// and the base size for the worst case (32 dimensional MdArray) is less than 0xffff.
|
||||
ldrh r2, [r0, #OFFSETOF__EEType__m_usComponentSize]
|
||||
cmp r1, #0x10000
|
||||
bhi LOCAL_LABEL(ArraySizeBig)
|
||||
|
@ -177,6 +250,9 @@ LOCAL_LABEL(ArrayAlignSize):
|
|||
EPILOG_POP "{r4-r6,pc}"
|
||||
|
||||
LOCAL_LABEL(ArraySizeBig):
|
||||
// if the element count is negative, it's an overflow error
|
||||
cmp r1, #0
|
||||
blt LOCAL_LABEL(ArraySizeOverflow)
|
||||
|
||||
// now we know the element count is in the signed int range [0..0x7fffffff]
|
||||
// overflow in computing the total size of the array size gives an out of memory exception,
|
||||
|
|
|
@ -113,6 +113,71 @@ NewOutOfMemory
|
|||
NESTED_END RhpNewObject
|
||||
|
||||
|
||||
;; Allocate a string.
|
||||
;; r0 == EEType
|
||||
;; r1 == element/character count
|
||||
LEAF_ENTRY RhNewString
|
||||
|
||||
; Make sure computing the overall allocation size won't overflow
|
||||
MOV32 r2, ((0xFFFFFFFF - STRING_BASE_SIZE - 3) / STRING_COMPONENT_SIZE)
|
||||
cmp r1, r2
|
||||
bhs StringSizeOverflow
|
||||
|
||||
; Compute overall allocation size (align(base size + (element size * elements), 4)).
|
||||
mov r2, #(STRING_BASE_SIZE + 3)
|
||||
#if STRING_COMPONENT_SIZE == 2
|
||||
add r2, r2, r1, lsl #1 ; r2 += characters * 2
|
||||
#else
|
||||
NotImplementedComponentSize
|
||||
#endif
|
||||
bic r2, r2, #3
|
||||
|
||||
; r0 == EEType
|
||||
; r1 == element count
|
||||
; r2 == string size
|
||||
|
||||
INLINE_GETTHREAD r3, r12
|
||||
|
||||
;; Load potential new object address into r12.
|
||||
ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr]
|
||||
|
||||
;; Determine whether the end of the object would lie outside of the current allocation context. If so,
|
||||
;; we abandon the attempt to allocate the object directly and fall back to the slow helper.
|
||||
adds r2, r12
|
||||
bcs RhpNewArrayRare ; if we get a carry here, the array is too large to fit below 4 GB
|
||||
ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_limit]
|
||||
cmp r2, r12
|
||||
bhi RhpNewArrayRare
|
||||
|
||||
;; Reload new object address into r12.
|
||||
ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr]
|
||||
|
||||
;; Update the alloc pointer to account for the allocation.
|
||||
str r2, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr]
|
||||
|
||||
;; Set the new object's EEType pointer and element count.
|
||||
str r0, [r12, #OFFSETOF__Object__m_pEEType]
|
||||
str r1, [r12, #OFFSETOF__String__m_Length]
|
||||
|
||||
;; Return the object allocated in r0.
|
||||
mov r0, r12
|
||||
|
||||
bx lr
|
||||
|
||||
StringSizeOverflow
|
||||
; We get here if the size of the final string object can't be represented as an unsigned
|
||||
; 32-bit value. We're going to tail-call to a managed helper that will throw
|
||||
; an OOM exception that the caller of this allocator understands.
|
||||
|
||||
; r0 holds EEType pointer already
|
||||
mov r1, #0 ; Indicate that we should throw OOM.
|
||||
b RhExceptionHandling_FailedAllocation
|
||||
|
||||
LEAF_END RhpNewString
|
||||
|
||||
INLINE_GETTHREAD_CONSTANT_POOL
|
||||
|
||||
|
||||
;; Allocate one dimensional, zero based array (SZARRAY).
|
||||
;; r0 == EEType
|
||||
;; r1 == element count
|
||||
|
@ -121,7 +186,7 @@ NewOutOfMemory
|
|||
; Compute overall allocation size (align(base size + (element size * elements), 4)).
|
||||
; if the element count is <= 0x10000, no overflow is possible because the component
|
||||
; size is <= 0xffff (it's an unsigned 16-bit value) and thus the product is <= 0xffff0000
|
||||
; and the base size is only 12 bytes.
|
||||
; and the base size for the worst case (32 dimensional MdArray) is less than 0xffff.
|
||||
ldrh r2, [r0, #OFFSETOF__EEType__m_usComponentSize]
|
||||
cmp r1, #0x10000
|
||||
bhi ArraySizeBig
|
||||
|
|
|
@ -236,6 +236,13 @@ $Name
|
|||
#endif
|
||||
MEND
|
||||
|
||||
;; Loads a 32bit constant into destination register
|
||||
MACRO
|
||||
MOV32 $destReg, $constant
|
||||
|
||||
movw $destReg, #(($constant) & 0xFFFF)
|
||||
movt $destReg, #(($constant) >> 16)
|
||||
MEND
|
||||
|
||||
;;
|
||||
;; CONSTANTS -- SYMBOLS
|
||||
|
|
|
@ -161,6 +161,135 @@ NewFinalizable_OOM:
|
|||
|
||||
FASTCALL_ENDFUNC
|
||||
|
||||
;; Allocate a new string.
|
||||
;; ECX == EEType
|
||||
;; EDX == element count
|
||||
FASTCALL_FUNC RhNewString, 8
|
||||
|
||||
push ecx
|
||||
push edx
|
||||
|
||||
;; Make sure computing the aligned overall allocation size won't overflow
|
||||
cmp edx, ((0FFFFFFFFh - STRING_BASE_SIZE - 3) / STRING_COMPONENT_SIZE)
|
||||
ja StringSizeOverflow
|
||||
|
||||
; Compute overall allocation size (align(base size + (element size * elements), 4)).
|
||||
lea eax, [(edx * STRING_COMPONENT_SIZE) + (STRING_BASE_SIZE + 3)]
|
||||
and eax, -4
|
||||
|
||||
; ECX == EEType
|
||||
; EAX == allocation size
|
||||
; EDX == scratch
|
||||
|
||||
INLINE_GETTHREAD edx, ecx ; edx = GetThread(), TRASHES ecx
|
||||
|
||||
; ECX == scratch
|
||||
; EAX == allocation size
|
||||
; EDX == thread
|
||||
|
||||
mov ecx, eax
|
||||
add eax, [edx + OFFSETOF__Thread__m_alloc_context__alloc_ptr]
|
||||
jc StringAllocContextOverflow
|
||||
cmp eax, [edx + OFFSETOF__Thread__m_alloc_context__alloc_limit]
|
||||
ja StringAllocContextOverflow
|
||||
|
||||
; ECX == allocation size
|
||||
; EAX == new alloc ptr
|
||||
; EDX == thread
|
||||
|
||||
; set the new alloc pointer
|
||||
mov [edx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], eax
|
||||
|
||||
; calc the new object pointer
|
||||
sub eax, ecx
|
||||
|
||||
pop edx
|
||||
pop ecx
|
||||
|
||||
; set the new object's EEType pointer and element count
|
||||
mov [eax + OFFSETOF__Object__m_pEEType], ecx
|
||||
mov [eax + OFFSETOF__String__m_Length], edx
|
||||
ret
|
||||
|
||||
StringAllocContextOverflow:
|
||||
; ECX == string size
|
||||
; original ECX pushed
|
||||
; original EDX pushed
|
||||
|
||||
; Re-push original ECX
|
||||
push [esp + 4]
|
||||
|
||||
; Create EBP frame.
|
||||
mov [esp + 8], ebp
|
||||
lea ebp, [esp + 8]
|
||||
|
||||
PUSH_COOP_PINVOKE_FRAME edx
|
||||
|
||||
; Preserve the string size in edi
|
||||
mov edi, ecx
|
||||
|
||||
; Get the EEType and put it in ecx.
|
||||
mov ecx, dword ptr [ebp - 8]
|
||||
|
||||
; Push alloc helper arguments (thread, size, flags, EEType).
|
||||
push edx ; transition frame
|
||||
push edi ; Size
|
||||
xor edx, edx ; Flags
|
||||
;; Passing EEType in ecx
|
||||
|
||||
;; void* RhpGcAlloc(EEType *pEEType, UInt32 uFlags, UIntNative cbSize, void * pTransitionFrame)
|
||||
call RhpGcAlloc
|
||||
|
||||
; Set the new object's EEType pointer and length on success.
|
||||
test eax, eax
|
||||
jz StringOutOfMemoryWithFrame
|
||||
|
||||
mov ecx, [ebp - 8]
|
||||
mov edx, [ebp - 4]
|
||||
mov [eax + OFFSETOF__Object__m_pEEType], ecx
|
||||
mov [eax + OFFSETOF__String__m_Length], edx
|
||||
|
||||
;; If the object is bigger than RH_LARGE_OBJECT_SIZE, we must publish it to the BGC
|
||||
cmp edi, RH_LARGE_OBJECT_SIZE
|
||||
jb NewString_SkipPublish
|
||||
mov ecx, eax ;; ecx: object
|
||||
mov edx, edi ;; edx: object size
|
||||
call RhpPublishObject ;; eax: this function returns the object that was passed-in
|
||||
NewString_SkipPublish:
|
||||
|
||||
POP_COOP_PINVOKE_FRAME
|
||||
add esp, 8 ; pop ecx / edx
|
||||
pop ebp
|
||||
ret
|
||||
|
||||
StringOutOfMemoryWithFrame:
|
||||
; This is the OOM failure path. We're going to tail-call to a managed helper that will throw
|
||||
; an out of memory exception that the caller of this allocator understands.
|
||||
|
||||
mov eax, [ebp - 8] ; Preserve EEType pointer over POP_COOP_PINVOKE_FRAME
|
||||
|
||||
POP_COOP_PINVOKE_FRAME
|
||||
add esp, 8 ; pop ecx / edx
|
||||
pop ebp ; restore ebp
|
||||
|
||||
mov ecx, eax ; EEType pointer
|
||||
xor edx, edx ; Indicate that we should throw OOM.
|
||||
jmp RhExceptionHandling_FailedAllocation
|
||||
|
||||
StringSizeOverflow:
|
||||
;; We get here if the size of the final string object can't be represented as an unsigned
|
||||
;; 32-bit value. We're going to tail-call to a managed helper that will throw
|
||||
;; an OOM exception that the caller of this allocator understands.
|
||||
|
||||
add esp, 8 ; pop ecx / edx
|
||||
|
||||
;; ecx holds EEType pointer already
|
||||
xor edx, edx ; Indicate that we should throw OOM.
|
||||
jmp RhExceptionHandling_FailedAllocation
|
||||
|
||||
FASTCALL_ENDFUNC
|
||||
|
||||
|
||||
;; Allocate one dimensional, zero based array (SZARRAY).
|
||||
;; ECX == EEType
|
||||
;; EDX == element count
|
||||
|
@ -171,7 +300,8 @@ FASTCALL_FUNC RhpNewArray, 8
|
|||
|
||||
; Compute overall allocation size (align(base size + (element size * elements), 4)).
|
||||
; 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
|
||||
; <= 0xffff, and thus the product is <= 0xffff0000, and the base size for the worst case
|
||||
; (32 dimensional MdArray) is less than 0xffff.
|
||||
movzx eax, word ptr [ecx + OFFSETOF__EEType__m_usComponentSize]
|
||||
cmp edx,010000h
|
||||
ja ArraySizeBig
|
||||
|
|
|
@ -171,6 +171,13 @@ COOP_PINVOKE_HELPER(Array *, RhpNewArray, (EEType * pArrayEEType, int numElement
|
|||
return pObject;
|
||||
}
|
||||
|
||||
COOP_PINVOKE_HELPER(String *, RhpNewString, (EEType * pArrayEEType, int numElements))
|
||||
{
|
||||
// TODO: Implement. We tail call to RhpNewArray for now since there's a bunch of TODOs in the places
|
||||
// that matter anyway.
|
||||
return (String*)RhpNewArray(pArrayEEType, numElements);
|
||||
}
|
||||
|
||||
#ifdef _ARM_
|
||||
COOP_PINVOKE_HELPER(Object *, RhpNewFinalizableAlign8, (EEType* pEEType))
|
||||
{
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#define PLAT_ASM_OFFSET(offset, cls, member) .set OFFSETOF__##cls##__##member, 0x##offset
|
||||
#define PLAT_ASM_SIZEOF(size, cls ) .set SIZEOF__##cls, 0x##size
|
||||
#define PLAT_ASM_CONST(constant, expr) .set expr, 0x##constant
|
||||
#define HASH_DEFINE #define
|
||||
#define PLAT_ASM_OFFSET(offset, cls, member) HASH_DEFINE OFFSETOF__##cls##__##member 0x##offset
|
||||
#define PLAT_ASM_SIZEOF(size, cls ) HASH_DEFINE SIZEOF__##cls 0x##size
|
||||
#define PLAT_ASM_CONST(constant, expr) HASH_DEFINE expr 0x##constant
|
||||
|
||||
#include <AsmOffsets.h>
|
||||
|
|
|
@ -191,6 +191,12 @@ C_FUNC(\Name):
|
|||
#endif
|
||||
.endm
|
||||
|
||||
// Loads a 32bit constant into destination register
|
||||
.macro MOV32 DestReg, Constant
|
||||
movw \DestReg, #((\Constant) & 0xFFFF)
|
||||
movt \DestReg, #((\Constant) >> 16)
|
||||
.endm
|
||||
|
||||
.macro EXPORT_POINTER_TO_ADDRESS Name
|
||||
|
||||
1:
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
#ifdef _ARM_
|
||||
|
||||
#define PLAT_ASM_OFFSET(offset, cls, member) OFFSETOF__##cls##__##member equ 0x##offset
|
||||
#define PLAT_ASM_SIZEOF(size, cls ) SIZEOF__##cls equ 0x##size
|
||||
#define PLAT_ASM_CONST(constant, expr) expr equ 0x##constant
|
||||
#define HASH_DEFINE #define
|
||||
#define PLAT_ASM_OFFSET(offset, cls, member) HASH_DEFINE OFFSETOF__##cls##__##member 0x##offset
|
||||
#define PLAT_ASM_SIZEOF(size, cls ) HASH_DEFINE SIZEOF__##cls 0x##size
|
||||
#define PLAT_ASM_CONST(constant, expr) HASH_DEFINE expr 0x##constant
|
||||
|
||||
#else
|
||||
|
||||
|
|
|
@ -134,7 +134,6 @@ internal static extern bool System.Runtime.RuntimeImports.RhIsPromoted(object ob
|
|||
internal static extern bool System.Runtime.RuntimeImports.RhIsServerGc()
|
||||
internal static extern object System.Runtime.RuntimeImports.RhMemberwiseClone(object obj)
|
||||
internal static extern System.Array System.Runtime.RuntimeImports.RhNewArray(System.EETypePtr pEEType, int length)
|
||||
internal static extern string System.Runtime.RuntimeImports.RhNewArrayAsString(System.EETypePtr pEEType, int length)
|
||||
internal static extern System.IntPtr System.Runtime.RuntimeImports.RhNewInterfaceDispatchCell(System.EETypePtr pEEType, int slotNumber)
|
||||
internal static extern object System.Runtime.RuntimeImports.RhNewObject(System.EETypePtr pEEType)
|
||||
internal static extern void System.Runtime.RuntimeImports.RhpEtwExceptionThrown(char* exceptionTypeName, char* exceptionMessage, System.IntPtr faultingIP, long hresult)
|
||||
|
|
|
@ -344,10 +344,9 @@ namespace System.Runtime
|
|||
[RuntimeImport(RuntimeLibrary, "RhNewArray")]
|
||||
internal static extern Array RhNewArray(EETypePtr pEEType, int length);
|
||||
|
||||
// @todo: Should we just have a proper export for this?
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
[RuntimeImport(RuntimeLibrary, "RhNewArray")]
|
||||
internal static extern String RhNewArrayAsString(EETypePtr pEEType, int length);
|
||||
[RuntimeImport(RuntimeLibrary, "RhNewString")]
|
||||
internal static extern String RhNewString(EETypePtr pEEType, int length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
[RuntimeImport(RuntimeLibrary, "RhBox")]
|
||||
|
|
|
@ -606,19 +606,12 @@ namespace System
|
|||
|
||||
internal static String FastAllocateString(int length)
|
||||
{
|
||||
try
|
||||
{
|
||||
// We allocate one extra char as an interop convenience so that our strings are null-
|
||||
// terminated, however, we don't pass the extra +1 to the array allocation because the base
|
||||
// size of this object includes the _firstChar field.
|
||||
string newStr = RuntimeImports.RhNewArrayAsString(EETypePtr.EETypePtrOf<string>(), length);
|
||||
Debug.Assert(newStr._stringLength == length);
|
||||
return newStr;
|
||||
}
|
||||
catch (OverflowException)
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
// We allocate one extra char as an interop convenience so that our strings are null-
|
||||
// terminated, however, we don't pass the extra +1 to the string allocation because the base
|
||||
// size of this object includes the _firstChar field.
|
||||
string newStr = RuntimeImports.RhNewString(EETypePtr.EETypePtrOf<string>(), length);
|
||||
Debug.Assert(newStr._stringLength == length);
|
||||
return newStr;
|
||||
}
|
||||
|
||||
internal static unsafe void wstrcpy(char* dmem, char* smem, int charCount)
|
||||
|
|
Загрузка…
Ссылка в новой задаче