-
Notifications
You must be signed in to change notification settings - Fork 508
Conversation
Debug.Assert(length >= 0); | ||
|
||
TypeDesc arrayType = ((TypeDesc)_methodIL.GetObject(token)).MakeArrayType(); | ||
Array array = RuntimeAugments.NewArray(arrayType.GetRuntimeTypeHandle(), length); |
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.
This doesn't work for non primitive types. Something like new DateTime[10]
fails because the runtime is unable to find the RuntimeTypeHandle
for DateTime[]
even though there exists one for DateTime
. Is there a way to probe the metadata of the interpreted assembly (and its dependencies) for runtime type information?
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.
It should work for arbitrary reference type arrays - value types are harder.
You should be able to work around by adding this array to RD.XML or adding typeof(DateTime[])
somewhere in the code that hosts the interpreter.
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.
Hmm....didn't work when I tried it with an Exception
type. I'll just add a TODO to add support for arbitrary types
array.SetValue((sbyte)valueItem.AsInt32(), index); | ||
break; | ||
case ILOpcode.stelem_i2: | ||
array.SetValue((short)valueItem.AsInt32(), index); |
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've preferred to use the generic type Array<T>
to prevent unnecessary boxing but when I do, ILC fails to compile the CoreRT console app with the following error:
Assertion Failed
Asking for Array<T> EEType
at ILCompiler.DependencyAnalysis.EETypeNode.OnMarked(NodeFactory context) in C:\Users\toni\Workspace\corert\src\ILCompiler.Compiler\src\Compiler\DependencyAnalysis\EETypeNode.c
s:line 1087
at ILCompiler.DependencyAnalysisFramework.DependencyNodeCore`1.CallOnMarked(DependencyContextType context) in C:\Users\toni\Workspace\corert\src\ILCompiler.DependencyAnalysisFr
amework\src\DependencyNodeCore.cs:line 136
at ILCompiler.DependencyAnalysisFramework.DependencyAnalyzer`2.AddToMarkStack(DependencyNodeCore`1 node, String reason, DependencyNodeCore`1 reason1, DependencyNodeCore`1 reaso
n2) in C:\Users\toni\Workspace\corert\src\ILCompiler.DependencyAnalysisFramework\src\DependencyAnalyzer.cs:line 313
at ILCompiler.DependencyAnalysisFramework.DependencyAnalyzer`2.GetStaticDependenciesImpl(DependencyNodeCore`1 node) in C:\Users\toni\Workspace\corert\src\ILCompiler.DependencyA
nalysisFramework\src\DependencyAnalyzer.cs:line 150
at ILCompiler.DependencyAnalysisFramework.DependencyAnalyzer`2.ComputeMarkedNodes() in C:\Users\toni\Workspace\corert\src\ILCompiler.DependencyAnalysisFramework\src\DependencyA
nalyzer.cs:line 266
at ILCompiler.RyuJitCompilation.CompileInternal(String outputFile, ObjectDumper dumper) in C:\Users\toni\Workspace\corert\src\ILCompiler.RyuJit\src\Compiler\RyuJitCompilation.c
s:line 40
at ILCompiler.Compilation.ILCompiler.ICompilation.Compile(String outputFile, ObjectDumper dumper) in C:\Users\toni\Workspace\corert\src\ILCompiler.Compiler\src\Compiler\Compila
tion.cs:line 387
at ILCompiler.Program.Run(String[] args) in C:\Users\toni\Workspace\corert\src\ILCompiler\src\Program.cs:line 575
at ILCompiler.Program.Main(String[] args) in C:\Users\toni\Workspace\corert\src\ILCompiler\src\Program.cs:line 731
The runtime builds successfully
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.
Array<T>
is just a type that we have in the corelib so that we can write methods that support the generic interfaces on arrays. We take it's vtable and use it as the vtable of T[]
. The type itself never exists. Even if we were to remove the check in the compiler, you wouldn't be able to cast to it at runtime because Array<T>
is different from T[]
.
You can call GetRawArrayData()
to get a byref to the first element of the array and then use Unsafe
class to do pointer arithmetic and writes/reads. Note that it will require adding bounds checks.
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.
GetArrayData
is internal to System.Private.CoreLib
so I can't use it
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 wouldn't mind making it public. The other option is to expose it through the RuntimeAugments class which is how we do it for other corelib internal APIs (reasons for RuntimeAugments are historic but we still do it for consistency).
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.
Is this what you have in mind?
ref byte start = ref RuntimeAugments.GetRawDataForArray(array);
ref byte position = ref Unsafe.Add(ref start, index);
// Read from int[]
Unsafe.Read<int>(ref position);
// Write to int[]
Unsafe.Write<int>(ref position, 23);
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.
Is this what you have in mind?
We need to multiply index by element size (available as Array.ElementSize
in the CoreLib), but yes, that's the 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.
Maybe use Unsafe.As
to cast to the right element type and avoid the Unsafe.Read
and Unsafe.Write
. I don't know whether Unsafe.Write
does the right write barriers if the element is a reference type.
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 don't know whether
Unsafe.Write
does the right write barriers if the element is a reference type.
It works fine.
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.
Thanks! Array.ElementSize
is definitely what I was missing
src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs
Show resolved
Hide resolved
index = indexItem.AsInt32(); | ||
break; | ||
case StackValueKind.NativeInt: | ||
index = (int)indexItem.AsNativeInt(); |
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.
This needs bounds check.
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.
Do you mean the conversion from IntPtr
to int
or the the value of index
relating to the array length (I already have this in my local branch)?
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.
Both of them.
Unsafe.Write(ref position, valueItem.AsDouble()); | ||
break; | ||
case ILOpcode.stelem_ref: | ||
Unsafe.Write(ref position, valueItem.AsObjectRef()); |
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.
This needs assignability check. It should end up calling System.Runtime.TypeCast.StelemRef
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.
Either need to expose the method to do this via RuntimeAugments; or cast the array to object[]
and assign it (Array.SetValue
does this).
case StackValueKind.NativeInt: | ||
{ | ||
long value = (long)indexItem.AsNativeInt(); | ||
Debug.Assert(value >= int.MinValue && value <= int.MaxValue); |
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.
This should throw IndexOutOfRangeException
, not assert.
break; | ||
} | ||
|
||
if (index < 0 || index >= array.Length) |
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.
Nit: This can be if ((uint)index >= (uint)array.Length)
to make it a bit faster. We use this trick frequently for array bound checks.
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.
Maybe this check can be inside GetSzArrayElementAddress
.
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.
We use this trick frequently for array bound checks.
Oh I saw it on Dictionary, is there also some special jit treatment if it recognize pattern?
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.
It is one comparison instead of two. It would be nice if the JIT recognized the two comparisons and converted them into one, but we are not there yet.
@@ -158,6 +164,17 @@ public static unsafe Array NewMultiDimArray(RuntimeTypeHandle typeHandleForArray | |||
return Array.NewMultiDimArray(typeHandleForArrayType.ToEETypePtr(), pLengths, lengths.Length); | |||
} | |||
|
|||
public static void StelemRef(Array array, int index, object obj) | |||
{ | |||
TypeCast.StelemRef(array, index, obj); |
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 suspect that this won't compile in the closed source ProjectN build (the Runtime,Base is not part of CoreLib in that build). @MichalStrehovsky Do you agree?
I think it would be easier to delete this method; and replace the calls with Unsafe.As<Object[]>(array)[index] = obj;
.
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.
Yeah, it wouldn't compile. Unsafe.As sounds good!
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.
Thank you!
This PR adds support for single-dimension zero-based arrays. It adds interpretation of the following opcodes:
newarr
ldlen
stelem.*
ldelem.*
which allows methods like the following to be interpreted: