Preliminary Interpreter Support#6182
Conversation
| PEInfo peinfo = new PEInfo(refName, null, null); | ||
| s_ecmaLoadedAssemblies.Add(peinfo); | ||
| } | ||
| // if (!foundMatch) |
There was a problem hiding this comment.
Should this code be put back?
There was a problem hiding this comment.
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.
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.
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.
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.
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.
For instance methods, you'll likely need another slot (unless the call interceptor does that for you)
There was a problem hiding this comment.
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.
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).
| ExecutionEnvironment = executionEnvironment; | ||
|
|
||
| #if SUPPORT_JIT | ||
| #if SUPPORT_INTERPRETER |
There was a problem hiding this comment.
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.
| <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.
Do we need this in CoreLib?
There was a problem hiding this comment.
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
| <AssemblyName>System.Private.Reflection.Core.Experimental</AssemblyName> | ||
| <EcmaMetadataSupport>true</EcmaMetadataSupport> | ||
| <JitSupport>true</JitSupport> | ||
| <InterpreterSupport>true</InterpreterSupport> |
There was a problem hiding this comment.
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.
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.
On a third look, nevermind 😄
| DynamicMethodHandleInfo* methodData = (DynamicMethodHandleInfo*)runtimeMethodHandleValue.ToPointer(); | ||
| declaringTypeHandle = *(RuntimeTypeHandle*)&(methodData->DeclaringType); | ||
| genericMethodArgs = null; | ||
| genericMethodArgs = new RuntimeTypeHandle[0]; |
There was a problem hiding this comment.
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.
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
davidwrighton
left a comment
There was a problem hiding this comment.
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.
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 |
MichalStrehovsky
left a comment
There was a problem hiding this comment.
Looks good to me modulo the two small things.
| <OutputType>Library</OutputType> | ||
| <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||
| </PropertyGroup> | ||
| <ItemGroup> |
There was a problem hiding this comment.
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.
| <Compile Include="Internal\Runtime\JitSupport\RyuJitExecutionStrategy.cs" /> | ||
| <Compile Include="Internal\Runtime\JitSupport\VirtualMethodSlotHelper.cs" /> | ||
| </ItemGroup> | ||
| <ItemGroup /> |
There was a problem hiding this comment.
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
Releasemode because the interpreter currently only supportsldc.i*andretopcodes andDebugmode introducesldlocandstlocopcodes. This will cease to be a requirement once the interpreter supports more opcodes.MainmethodThis code loads the assembly in
(1), retrieves itsEntryPointand invokes it at runtime all within the CoreRT runtime with the help of the interpreter. Add anrd.xmlfile to the CoreRT project with the following contents:Include the
rd.xmlin 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,
100will 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.EmitAPI can be updated to take advantage of the interpreter functionality.#5011
cc @jkotas @MichalStrehovsky