-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Implement Array.Initialize in C# #77336
Conversation
I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label. |
Are changes needed in Mono? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will need to do something about the failing tests ... Array.Initialize is implemented on CoreCLR only currently.
The mono changes look ok. |
Test failures were caused by a System.Reflection.MetadataLoadContext test that scans all methods on an array type. Since a local variable of function pointer type was added to a method on System.Array, MLC would fail when decoding this local variable. Fortunately, the fix is pretty trivial, since we have a convenient place to move that to. |
@@ -381,8 +381,58 @@ private unsafe bool IsValueOfElementType(object value) | |||
// if this is an array of value classes and that value class has a default constructor | |||
// then this calls this default constructor on every element in the value class array. | |||
// otherwise this is a no-op. Generally this method is called automatically by the compiler | |||
[MethodImpl(MethodImplOptions.InternalCall)] | |||
public extern void Initialize(); | |||
public unsafe void Initialize() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dotnet/ilc-contrib Should Array.Initialize
be marked with RequiresUnreferenceCode
, or is the value type default constructor the special-cased by the trimmer and always preserved?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should:
var b = new ElementType[1];
b.Initialize();
struct ElementType
{
public ElementType()
{
Console.WriteLine(".ctor called");
}
}
dotnet run
prints out ".ctor called".
But trimmed the app doesn't print out anything.
Related question - what should default
do in this case, for example:
var c = new ElementType[1];
c[0] = default; // Should this call the default .ctor?
Running it seems like it will NOT call the .ctor - but what does it do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same goes for AOT - published as Native AOT the app above also doesn't print out anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related question - what should default do in this case, for example:
According to the specification, default
ignores the parameterless constructor and generates a zeroed instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, adding RequiresUnreferencedCode
on Array.Initialize
has a ripple effect. It introduces warnings in situations where array type is passed into DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)
.
I guess it will need more careful thought.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened #77426
LGTM modulo comments. Thank you! Another 100 lines of manually managed C++ bite the dust. |
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs
Outdated
Show resolved
Hide resolved
@@ -233,6 +233,31 @@ ves_icall_System_Array_SetValueRelaxedImpl (MonoArrayHandle arr, MonoObjectHandl | |||
array_set_value_impl (arr, value, pos, FALSE, FALSE, error); | |||
} | |||
|
|||
void | |||
ves_icall_System_Array_InitializeInternal (MonoObjectHandleOnStack arr_handle, MonoError *error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@reflectronic Are you up for implementing the mono version mostly in C#, too?
I think we can add something like NativeAOT's default constructor -> function pointer icall and then most of this code could be managed.
We don't need to do it in this PR (unless you want to). Just an idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would have done it like that, but I couldn't quite find the function I wanted (wasn't sure if the thing I was looking at was safe to just call with calli
, whether it would try to unbox the argument, etc.). Certainly, I could address it in a follow up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@reflectronic I don't think there's an existing icall, but it should be ok to add something like this:
MonoMethod * const method = mono_class_get_method_from_name_checked (element_class, ".ctor", 0, 0, error);
if (!method) {
return NULL;
}
gpointer addr = mono_compile_method_checked (method, error);
if (!is_ok (error)) {
mono_error_cleanup (error);
return NULL;
}
return mono_create_ftnptr(addr);
on the C# side it would look pretty much like the NativeAOT version - cast the icall's IntPtr
result to a function pointer type and call it.
wasn't sure if the thing I was looking at was safe to just call with calli, whether it would try to unbox the argument, etc.
I'm pretty certain that it will just work. I believe it should be expecting an unboxed argument.
The failure is known nuget infrastructure issue |
Thanks for the reviews and feedback |
Implements
Array.Initialize
for Mono, moves most of the implementation to managed code for CoreCLR, and adds a test.For those using C++/CLI, your arrays shall now be initialized marginally faster: