Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve performance of Activator.CreateInstance #32520

Merged
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
d0bde83
Fast object instantiation, take two
GrabYourPitchforks May 9, 2020
67539c2
Merge remote-tracking branch 'origin/master' into fast_createinstance…
GrabYourPitchforks Jul 8, 2020
edf3858
PR feedback
GrabYourPitchforks Jul 7, 2020
c5ae79f
Re-enable analyzers in project
GrabYourPitchforks Jul 8, 2020
e09e91b
Don't run COM tests on non-Windows
GrabYourPitchforks Jul 8, 2020
17a3bde
PR feedback
GrabYourPitchforks Jul 8, 2020
2467fdd
Fix mergeMerge remote-tracking branch 'origin/master' into fast_creat…
GrabYourPitchforks Nov 17, 2020
eb368eb
Cleanup + PR feedback
GrabYourPitchforks Nov 17, 2020
b85fb74
Merge remote-tracking branch 'origin/master' into fast_createinstance
GrabYourPitchforks Nov 18, 2020
faeef8c
Remove 'unwrapNullable' parameter
GrabYourPitchforks Nov 18, 2020
bdb403a
Quick GetDefaultCtor helper
GrabYourPitchforks Nov 18, 2020
c173fda
Remove unmanaged Allocate / CreateInstance
GrabYourPitchforks Nov 18, 2020
5c52acd
Fix GetDefaultConstructor signature
GrabYourPitchforks Nov 19, 2020
a1c362c
Fix AV in allocator
GrabYourPitchforks Nov 19, 2020
3ce9c41
Fix indentation
GrabYourPitchforks Nov 19, 2020
904b25d
Fix COM instantiation
GrabYourPitchforks Nov 19, 2020
1f6434a
Remove COM allocator special-case
GrabYourPitchforks Nov 19, 2020
c1ecb07
Fix mono build break; quick code cleanup
GrabYourPitchforks Nov 19, 2020
a071262
Rename GetNewobjHelperFnPtr -> GetAllocatorFtn
GrabYourPitchforks Nov 19, 2020
dc417fd
Cleanup in the allocator routines
GrabYourPitchforks Nov 19, 2020
6904495
Fix linker failures, add asserts
GrabYourPitchforks Nov 19, 2020
05c35a8
Remove unwanted CreateInstanceDefaultCtor overload
GrabYourPitchforks Nov 19, 2020
3d8515a
Use WeakRef in ActivatorCache
GrabYourPitchforks Nov 19, 2020
5acef41
Reintroduce COM specialization
GrabYourPitchforks Nov 20, 2020
adfae42
Allow runtime to create unboxing stub on my behalf
GrabYourPitchforks Nov 20, 2020
49a8de1
Fix linker warning
GrabYourPitchforks Nov 20, 2020
98c7941
Update error conditions
GrabYourPitchforks Nov 20, 2020
0a443c2
Friendlier exception messages when activation fails
GrabYourPitchforks Nov 20, 2020
3b34ae6
Cleanup usings
GrabYourPitchforks Nov 20, 2020
6e1b7cd
Move friendly exception messages to common place
GrabYourPitchforks Nov 20, 2020
b588d5d
Fix TIE wrappers
GrabYourPitchforks Nov 20, 2020
55aa128
Update unit tests
GrabYourPitchforks Nov 20, 2020
0ef275c
Call CreateInstanceCheckThis for better exception handling
GrabYourPitchforks Nov 20, 2020
4c1f5dd
PR feedback + fix failing coreclr unit tests
GrabYourPitchforks Nov 20, 2020
97f5f60
Fix build breaks
GrabYourPitchforks Nov 20, 2020
867bdc0
Merge remote-tracking branch 'levib/fast_createinstance' into fast_cr…
GrabYourPitchforks Nov 20, 2020
2e44013
Fix strings.resx whitespace
GrabYourPitchforks Nov 20, 2020
9c267fd
Fix build breaks on mono & non-Windows
GrabYourPitchforks Nov 20, 2020
bead4c6
PR feedback
GrabYourPitchforks Nov 20, 2020
8f12dd8
Fix GC hole in AllocateComObject
GrabYourPitchforks Nov 21, 2020
3a2021d
Refactor GetActivationInfo
GrabYourPitchforks Nov 22, 2020
46ed0b8
Refactor RunClassConstructor
GrabYourPitchforks Nov 22, 2020
d3bc632
Update GetUninitializedObject to call the new APIs
GrabYourPitchforks Nov 22, 2020
3f362f2
Hook up ActivatorCache to new system
GrabYourPitchforks Nov 22, 2020
879937b
Fix typo causing build break
GrabYourPitchforks Nov 22, 2020
93bcedb
Change GetActivationInfo to QCALL
GrabYourPitchforks Nov 22, 2020
e04401f
Simplify some call sites
GrabYourPitchforks Nov 22, 2020
9887e9a
Merge remote-tracking branch 'origin/master' into fast_createinstance…
GrabYourPitchforks Nov 22, 2020
186fd83
Fix failing tests
GrabYourPitchforks Nov 22, 2020
4d3d9db
Fix bad assert
GrabYourPitchforks Nov 22, 2020
036cbf0
Add managed _AllocateComObject member to make FCall checks happier
GrabYourPitchforks Nov 22, 2020
882b63e
Avoid using GetEEFuncEntryPoint
GrabYourPitchforks Nov 22, 2020
0b0c8db
Fix build error in ecalllist.h
GrabYourPitchforks Nov 22, 2020
c50d287
Update COM invocation unit tests
GrabYourPitchforks Nov 22, 2020
e2235cc
Merge remote-tracking branch 'origin/master' into fast_createinstance…
GrabYourPitchforks Nov 22, 2020
cebbf48
Allow propagation of PNSE in RuntimeHandles
GrabYourPitchforks Nov 22, 2020
d373fe7
Merge remote-tracking branch 'origin/master' into fast_createinstance…
GrabYourPitchforks Nov 24, 2020
12b4578
Remove _AllocateComObject sentinel
GrabYourPitchforks Nov 24, 2020
a7fa617
PR feedback
GrabYourPitchforks Nov 24, 2020
3e9438c
PR feedback - simplify GetActivationInfo out params
GrabYourPitchforks Nov 25, 2020
9c20d84
Merge remote-tracking branch 'origin/master' into fast_createinstance…
GrabYourPitchforks Nov 25, 2020
4b38b6c
Add missing ifdef around _AllocateComInterop
GrabYourPitchforks Nov 25, 2020
9ed2b9b
Final inspection + code cleanup
GrabYourPitchforks Nov 25, 2020
0ebebd6
Fix compilation error on non-Windows
GrabYourPitchforks Nov 25, 2020
374cf34
Code cleanup & PR feedback
GrabYourPitchforks Nov 25, 2020
6b23817
Merge remote-tracking branch 'origin/master' into fast_createinstance…
GrabYourPitchforks Nov 25, 2020
11be754
More code cleanup
GrabYourPitchforks Nov 26, 2020
5d43a91
Rename ActivatorCache source file
GrabYourPitchforks Nov 26, 2020
0635399
Remove unnecessary asserts, underscores, indentation
GrabYourPitchforks Nov 26, 2020
9ac3c3f
Fix [DynamicallyAccessedMembers] annotations
GrabYourPitchforks Nov 26, 2020
a06ef73
Fix bad assert
GrabYourPitchforks Nov 26, 2020
1b1261f
Update src/coreclr/src/vm/reflectioninvocation.cpp
GrabYourPitchforks Nov 26, 2020
94f8614
PR feedback
GrabYourPitchforks Nov 26, 2020
c73e9fa
Update src/coreclr/src/vm/reflectioninvocation.cpp
jkotas Nov 26, 2020
359a588
Remove incorrect assert in ActivatorCache
GrabYourPitchforks Nov 26, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ private static IArraySortHelper<T> CreateArraySortHelper()

if (typeof(IComparable<T>).IsAssignableFrom(typeof(T)))
{
defaultArraySortHelper = (IArraySortHelper<T>)RuntimeTypeHandle.Allocate(typeof(GenericArraySortHelper<string>).TypeHandle.Instantiate(new Type[] { typeof(T) }));
defaultArraySortHelper = (IArraySortHelper<T>)typeof(GenericArraySortHelper<string>).TypeHandle.Instantiate(new Type[] { typeof(T) }).CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: false)!;
}
else
{
Expand Down Expand Up @@ -62,7 +62,7 @@ private static IArraySortHelper<TKey, TValue> CreateArraySortHelper()

if (typeof(IComparable<TKey>).IsAssignableFrom(typeof(TKey)))
{
defaultArraySortHelper = (IArraySortHelper<TKey, TValue>)RuntimeTypeHandle.Allocate(typeof(GenericArraySortHelper<string, string>).TypeHandle.Instantiate(new Type[] { typeof(TKey), typeof(TValue) }));
defaultArraySortHelper = (IArraySortHelper<TKey, TValue>)typeof(GenericArraySortHelper<string, string>).TypeHandle.Instantiate(new Type[] { typeof(TKey), typeof(TValue) }).CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: false)!;
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1228,7 +1228,7 @@ private static void AddCustomAttributes(
}
else
{
attribute = attributeType.CreateInstanceDefaultCtor(publicOnly: false, skipCheckThis: true, fillCache: true, wrapExceptions: false)!;
attribute = attributeType.CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: false)!;

// It is allowed by the ECMA spec to have an empty signature blob
int blobLen = (int)((byte*)blobEnd - (byte*)blobStart);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;
using Internal.Runtime.CompilerServices;

Expand Down Expand Up @@ -135,15 +136,23 @@ public static int OffsetToStringData
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern bool TryEnsureSufficientExecutionStack();

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object GetUninitializedObjectInternal(
// This API doesn't call any constructors, but the type needs to be seen as constructed.
// A type is seen as constructed if a constructor is kept.
// This obviously won't cover a type with no constructor. Reference types with no
// constructor are an academic problem. Valuetypes with no constructors are a problem,
// but IL Linker currently treats them as always implicitly boxed.
private static unsafe object GetUninitializedObjectInternal(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
Type type);
Type type)
{
GrabYourPitchforks marked this conversation as resolved.
Show resolved Hide resolved
RuntimeType rt = (RuntimeType)type;
Debug.Assert(rt != null);
GrabYourPitchforks marked this conversation as resolved.
Show resolved Hide resolved

// If type is Nullable<T>, get newobj for boxed T instead.
delegate*<MethodTable*, object> newobjHelper = RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out MethodTable* pMT, unwrapNullable: true);
Debug.Assert(newobjHelper != null);
Debug.Assert(pMT != null);

object retVal = newobjHelper(pMT);
GC.KeepAlive(rt); // don't allow the type to be collected before the object is instantiated

return retVal;
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object AllocateUninitializedClone(object obj);
Expand Down Expand Up @@ -378,10 +387,16 @@ internal unsafe struct MethodTable
[FieldOffset(InterfaceMapOffset)]
public MethodTable** InterfaceMap;

// WFLAGS_HIGH_ENUM
// WFLAGS_HIGH_ENUM & WFLAGS_LOW_ENUM
private const uint enum_flag_Category_Mask = 0x000F0000;
private const uint enum_flag_Category_Nullable = 0x00050000;
private const uint enum_flag_Category_ValueType = 0x00040000;
private const uint enum_flag_Category_ValueType_Mask = 0x000C0000;
private const uint enum_flag_ContainsPointers = 0x01000000;
private const uint enum_flag_ComObject = 0x40000000;
private const uint enum_flag_HasComponentSize = 0x80000000;
private const uint enum_flag_HasTypeEquivalence = 0x00004000;
private const uint enum_flag_HasDefaultCtor = 0x00000200;
private const uint enum_flag_HasTypeEquivalence = 0x00004000; // TODO: shouldn't this be 0x02000000?
GrabYourPitchforks marked this conversation as resolved.
Show resolved Hide resolved
// Types that require non-trivial interface cast have this bit set in the category
private const uint enum_flag_NonTrivialInterfaceCast = 0x00080000 // enum_flag_Category_Array
| 0x40000000 // enum_flag_ComObject
Expand Down Expand Up @@ -438,6 +453,38 @@ public bool NonTrivialInterfaceCast
}
}

public bool HasDefaultConstructor
{
get
{
return (Flags & enum_flag_HasDefaultCtor) != 0;
}
}

public bool IsComObject
{
get
{
return (Flags & enum_flag_ComObject) != 0;
}
}

public bool IsNullable
{
get
{
return (Flags & enum_flag_Category_Mask) == enum_flag_Category_Nullable;
}
}

public bool IsValueType
{
get
{
return (Flags & enum_flag_Category_ValueType_Mask) == enum_flag_Category_ValueType;
}
}

public bool HasTypeEquivalence
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ private static void PrelinkCore(MethodInfo m)
private static object PtrToStructureHelper(IntPtr ptr, Type structureType)
{
var rt = (RuntimeType)structureType;
object structure = rt.CreateInstanceDefaultCtor(publicOnly: false, skipCheckThis: false, fillCache: false, wrapExceptions: true)!;
object structure = rt.CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: true)!;
PtrToStructureHelper(ptr, structure, allowValueClasses: true);
return structure;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,56 @@ internal static bool HasElementType(RuntimeType type)
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object CreateInstanceForAnotherGenericParameter(RuntimeType type, RuntimeType genericParameter);

/// <summary>
/// Given a RuntimeType, returns both the address of the JIT's newobj helper for that type and the
/// MethodTable* corresponding to that type. If the type is <see cref="Nullable{T}"/> closed over
/// some T, then returns the newobj helper and MethodTable* for the 'T'.
/// Return value signature is managed calli (MethodTable* pMT) -> object.
/// </summary>
internal static delegate*<MethodTable*, object> GetNewobjHelperFnPtr(
// This API doesn't call any constructors, but the type needs to be seen as constructed.
// A type is seen as constructed if a constructor is kept.
// This obviously won't cover a type with no constructor. Reference types with no
// constructor are an academic problem. Valuetypes with no constructors are a problem,
// but IL Linker currently treats them as always implicitly boxed.
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type,
out MethodTable* pMT, bool unwrapNullable)
{
Debug.Assert(type != null);

delegate*<MethodTable*, object> pNewobjHelperTemp = null;
MethodTable* pMTTemp = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a new variable here instead of just using pMT?

Copy link
Member Author

@GrabYourPitchforks GrabYourPitchforks Nov 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to pass a pointer (arg is typed as MethodTable**). Using a temp local was more straightforward than pinning the incoming pMT argument. Using locals also ensures that I can initialize both values to default, regardless of any shenanigans pulled by the caller.


GetNewobjHelperFnPtr(
new QCallTypeHandle(ref type),
&pNewobjHelperTemp,
&pMTTemp,
unwrapNullable ? Interop.BOOL.TRUE : Interop.BOOL.FALSE);

pMT = pMTTemp;
return pNewobjHelperTemp;
}

[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void GetNewobjHelperFnPtr(QCallTypeHandle typeHandle, delegate*<MethodTable*, object>* ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fUnwrapNullable);

/// <summary>
/// Returns the MethodDesc* for this type's parameterless instance ctor.
/// For reference types, signature is (object @this) -> void.
/// For value types, signature is (ref T @thisUnboxed) -> void.
/// Returns nullptr if no parameterless ctor is defined.
/// </summary>
internal static RuntimeMethodHandleInternal GetDefaultConstructor(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type)
{
Debug.Assert(type != null);

return GetDefaultCtor(new QCallTypeHandle(ref type));
}

[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern RuntimeMethodHandleInternal GetDefaultCtor(QCallTypeHandle typeHandle);

internal RuntimeType GetRuntimeType()
{
return m_type;
Expand Down
Loading