-
Notifications
You must be signed in to change notification settings - Fork 510
Preliminary Interpreter Support #6182
Preliminary Interpreter Support #6182
Conversation
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 looks like a great start, @tonerdo! I'm looking forward to seeing where the interpreter goes
PEInfo peinfo = new PEInfo(refName, null, null); | ||
s_ecmaLoadedAssemblies.Add(peinfo); | ||
} | ||
// if (!foundMatch) |
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.
Should this code be put back?
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.
The interpreter is gonna fail if this is still in there. Can't really say whether the code is essential for other scenarios which is why I just left it commented out for now
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.
Someone will need to understand the code. I didn't have a chance to look into it deeply yet. Maybe @davidwrighton could help us here? This code was added as part of support for satellite assemblies, but breaks the Assembly.Load(byte[])
scenario.
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'll admit that I don't know what satellite assemblies are. What scenarios are they typically used?
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.
https://blogs.msdn.microsoft.com/global_developer/2011/07/22/introduction-to-satellite-assemblies/ - you can put managed resourced for a specific locale in them. I'm not sure if this actually works with the CoreRT compiler, but it's a supported scenario for .NET Native for UWP apps (this code is shared with the closed source runtime/compiler).
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.
Sadly, this is necessary for satellite assembly work. What you'll need to do is add a Boolean flag to the function to indicate whether or not the caching should happen, and then thread it through such that for byte[] loads, the failure isn't cached when we do the speculative lookup to see if the assembly has already been loaded.
{ | ||
get | ||
{ | ||
LocalVariableType[] localVariableTypes = new LocalVariableType[_method.Signature.Length + 1]; |
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.
For instance methods, you'll likely need another slot (unless the call interceptor does that for you)
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, that's true. Didn't realize I'll have to explicitly handle the this
parameter. I'll leave it up to future testing to surface the need for it
private readonly MethodDesc _method; | ||
private readonly MethodIL _methodIL; | ||
private readonly TypeSystemContext _context; | ||
private readonly LowLevelStack<StackItem> _stack; |
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 @jkotas had ideas on what we'd need to do to make the evaluation stack work for byref types, so this might have to change at some point (it's fine for now).
@@ -64,8 +64,9 @@ internal static void Initialize() | |||
}; | |||
|
|||
ExecutionEnvironment = executionEnvironment; | |||
|
|||
#if SUPPORT_JIT | |||
#if SUPPORT_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.
This means that the .Experimental
version will no longer work with the JIT prototype (both SUPPORT_INTERPRETER and SUPPORT_JIT are defined there).
I think the fix should be to move the initialization of GlobalExecutionStrategy
out of reflection execution initialization, into Jit/Interpreter initialization. To do that, you would add this to both the S.P.Interpreter and S.P.Jit projects (with the obvious tweak):
namespace Internal.Runtime.CompilerHelpers
{
public class LibraryInitializer
{
public static void InitializeLibrary()
{
Internal.Runtime.TypeLoader.MethodExecutionStrategy.GlobalExecutionStrategy = new Internal.Runtime.Interpreter.InterpreterExecutionStrategy();
}
}
}
The compiler will make sure this is called at startup because S.P.Interpreter is one of the AutoInitializedAssemblies
.
@@ -622,6 +622,9 @@ | |||
<Compile Include="..\..\Common\src\System\Collections\Generic\LowLevelDictionary.cs"> | |||
<Link>System\Collections\Generic\LowLevelDictionary.cs</Link> | |||
</Compile> | |||
<Compile Include="..\..\Common\src\System\Collections\Generic\LowLevelStack.cs"> | |||
<Link>System\Collections\Generic\LowLevelStack.cs</Link> |
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 we need this in CoreLib?
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.
No we don't. I added it to be consistent with the inclusion of LowLevelList
. I'll remove it since we don't need it
@@ -3,6 +3,7 @@ | |||
<AssemblyName>System.Private.Reflection.Core.Experimental</AssemblyName> | |||
<EcmaMetadataSupport>true</EcmaMetadataSupport> | |||
<JitSupport>true</JitSupport> | |||
<InterpreterSupport>true</InterpreterSupport> |
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'm wondering if we should just call this ExperimentalSupport
/DynamicCodeSupport
, or something like that, and replace JitSupport
with that single flag. It doesn't seem like these assemblies need to know the details of the execution strategy, besides knowing that they have to reference the .Experimental
flavors of the assemblies.
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.
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.
On second look, it seems there are some parts of S.P.Typeloader
that are conditionally compiled based on the SUPPORT_JIT
constant being set when JitSupport
is true
. We ideally should not have these parts when we're just interested in having an interpreter which will be the case if we use a single tag to encompass both JIT and Interpreter support
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.
On a third look, nevermind 😄
@@ -363,7 +363,7 @@ private unsafe bool TryGetDynamicRuntimeMethodHandleComponents(RuntimeMethodHand | |||
|
|||
DynamicMethodHandleInfo* methodData = (DynamicMethodHandleInfo*)runtimeMethodHandleValue.ToPointer(); | |||
declaringTypeHandle = *(RuntimeTypeHandle*)&(methodData->DeclaringType); | |||
genericMethodArgs = null; | |||
genericMethodArgs = new RuntimeTypeHandle[0]; |
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.
Might want to replace this with Array.Empty<RuntimeTypeHandle>
to avoid the allocation. Do you remember where you hit this?
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.
Good point. I believe I hit this issue when I called ToMethodDesc
on TypeSystemExtensions
in my execution strategy. genericMethodArgs
is an out
parameter and although it's always null
we unconditionally try to access the Length
property here: https://github.com/dotnet/corert/blob/master/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemExtensions.cs#L148. I was always getting an access violation exception
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.
Looks great, but you need to avoid breaking the semantic that Assembly.Load operations cannot behave differently once they have settled on a result.
PEInfo peinfo = new PEInfo(refName, null, null); | ||
s_ecmaLoadedAssemblies.Add(peinfo); | ||
} | ||
// if (!foundMatch) |
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.
Sadly, this is necessary for satellite assembly work. What you'll need to do is add a Boolean flag to the function to indicate whether or not the caching should happen, and then thread it through such that for byte[] loads, the failure isn't cached when we do the speculative lookup to see if the assembly has already been loaded.
@davidwrighton take a look at my latest commit: 3cea4e0. Is this what you had in mind? |
Do we need the new parameter in method signatures that have the We also have a convention to use the named argument syntax for bool arguments that pass literal bool values (so |
@tonerdo That's generally what I had in mind, but you've passed false everywhere. The proper behavior is to cache missed lookups essentially everywhere except where it causes the byte[] scenario to fail to load entirely. So, I believe you need to pass true in every single place you marked as passing false, except for the first call in BindEcmaByteArray, which should pass false. |
I have a question about this PR. Is there any reason why the JIT support (in Looking at the commit history for |
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.
Looks good to me modulo the two small things.
<OutputType>Library</OutputType> | ||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||
</PropertyGroup> | ||
<ItemGroup> |
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.
Could you make this conditioned on Condition="'$(IsProjectNLibrary)' != 'true'"
, same as the other CSPROJ files? We need this to keep things buildable on the closed-source side.
@@ -154,5 +155,6 @@ | |||
<Compile Include="Internal\Runtime\JitSupport\RyuJitExecutionStrategy.cs" /> | |||
<Compile Include="Internal\Runtime\JitSupport\VirtualMethodSlotHelper.cs" /> | |||
</ItemGroup> | |||
<ItemGroup /> |
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: unnecessary ItemGroup
You can ignore my previous question re |
@MichalStrehovsky Anything left for me to do on this? |
It looks good to me - I'll merge this when the CI is finishes. |
@tonerdo Thanks for the patience! Now that the scaffolding is in place, the work within System.Private.Interpreter should go through reviews faster. |
The change to return an empty array instead of null broke a method handle test. Seems like generic arguments being null is a contract. Restoring the old logic and adding handling of null to the place that promted the change (#6182 (comment)). [tfs-changeset: 1711212]
The JIT prototype was originally brought up on the closed source ".NET Native for UWP apps" compiler/runtime, so this is mostly to adapt it for CoreRT. Surprisingly, this hasn't bit rotten much over time and some of the bitrot has been cleaned up by @tonerdo in his interpreter work (dotnet#6182). Both an interpreter and a JIT are a great addition to a fully AOT compiled runtime and they complement each other nicely. With this, I'm making the JIT prototype work on CoreRT: * Adjust the hacks that were used by JIT code manager to talk to the runtime (in CoreRT, runtime is linked into the executable and the hacks didn't work). This will need further cleanup at some point. * Rename ReaderWriterLock because it was conflicting with a type of the same name in the runtime. * Add an experimental flavor of the stack trace decoder. This needs to use the experimental type loader. * Expose the JIT component from MSBuild - compile JIT support into the executable, use the experimental flavor of the reflection stack (shared with the interpreter), add the extra native library with JIT support. This is to allow enabling JIT support as part of `dotnet publish`. You can try this out pretty much the same way as @tonerdo's interpreter: see instructions in dotnet#6182. Instead of passing `/p:ExperimentalInterpreterSupport=true`, pass `/p:ExperimentalJitSupport=true`. You'll need to manually copy clrjitilc.dll from the compiler to the publish directory (we use the same codegen that is used to compile the AOT parts of the application to also JIT compile at runtime).
The JIT prototype was originally brought up on the closed source ".NET Native for UWP apps" compiler/runtime, so this is mostly to adapt it for CoreRT. Surprisingly, this hasn't bit rotten much over time and some of the bitrot has been cleaned up by @tonerdo in his interpreter work (#6182). Both an interpreter and a JIT are a great addition to a fully AOT compiled runtime and they complement each other nicely. With this, I'm making the JIT prototype work on CoreRT: * Adjust the hacks that were used by JIT code manager to talk to the runtime (in CoreRT, runtime is linked into the executable and the hacks didn't work). This will need further cleanup at some point. * Rename ReaderWriterLock because it was conflicting with a type of the same name in the runtime. * Add an experimental flavor of the stack trace decoder. This needs to use the experimental type loader. * Expose the JIT component from MSBuild - compile JIT support into the executable, use the experimental flavor of the reflection stack (shared with the interpreter), add the extra native library with JIT support. This is to allow enabling JIT support as part of `dotnet publish`. You can try this out pretty much the same way as @tonerdo's interpreter: see instructions in #6182. Instead of passing `/p:ExperimentalInterpreterSupport=true`, pass `/p:ExperimentalJitSupport=true`. You'll need to manually copy clrjitilc.dll from the compiler to the publish directory (we use the same codegen that is used to compile the AOT parts of the application to also JIT compile at runtime).
publish the console like that "dotnet publish -r win-x64", it works normal. so is it default to use "/p:ExperimentalInterpreterSupport=true" for now? |
i found the reason is that i added rd.xml (<Assembly Name="System.Runtime" Dynamic="Required All" />). is it normal ? @tonerdo |
This PR includes the preliminary work needed to integrate an interpreter into CoreRT to support runtime code generation scenarios.
To test the current interpreter status:
You can return any arbitrary integer value, the project must be compiled in
Release
mode because the interpreter currently only supportsldc.i*
andret
opcodes andDebug
mode introducesldloc
andstloc
opcodes. This will cease to be a requirement once the interpreter supports more opcodes.Main
methodThis code loads the assembly in
(1)
, retrieves itsEntryPoint
and invokes it at runtime all within the CoreRT runtime with the help of the interpreter. Add anrd.xml
file to the CoreRT project with the following contents:Include the
rd.xml
in the build pipeline by adding it to the CoreRT csproj:Compile the CoreRT app with the experimental interpreter enabled
Run the resulting native executable. If all goes well,
100
will be printed to the terminal. You can update the return code of the main method in(1)
for further tests. You won't need to rebuild the CoreRT app, just the assembly that is loaded.The interpreter work is still in very early stages and I intend to put in further work to add support for all CLR opcodes. Dynamically loading a .NET assembly at runtime is the current scenario being tested, after which the
Reflection.Emit
API can be updated to take advantage of the interpreter functionality.#5011
cc @jkotas @MichalStrehovsky