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:
Michal Strehovsky 2017-08-14 10:59:13 -07:00
Родитель d714e3be0b
Коммит 1e9054a911
17 изменённых файлов: 484 добавлений и 35 удалений

Просмотреть файл

@ -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)