dubiousconst282 / DistIL

Post-build IL optimizer and intermediate representation for .NET programs
MIT License
116 stars 1 forks source link

Unity Support + Mono #36

Open DaZombieKiller opened 1 month ago

DaZombieKiller commented 1 month ago

First off, awesome project! I am happy to look into contributing parts of this feature if necessary.

Due to the fact that Unity does not use MSBuild or support NuGet, it is currently non-trivial to consume DistIL from it. Some Unity-specific code would need to be written that uses CompilationPipeline.assemblyCompilationFinished to run DistIL. As Unity only supports .NET Standard 2.1 at the latest, it would likely need to use DistIL.Cli rather than running it in-process.

The JIT codegen in legacy Mono (and by extension, Unity) is typically rather poor, but can be significantly improved with the right tricks. I see a lot of potential in this project for improving the situation.

Some potential optimization passes that come to mind are:

dubiousconst282 commented 1 month ago

Thanks for your interest. I'm not very familiar with the Unity ecosystem at this time so any patches would be welcome.

Invoking DistIL.Cli directly would be fine, and it's what the MSBuild task currently does. This is a bit wasteful since it has to reload and process everything from scratch every time, so it might become a problem if it needs to happen very frequently.

I should mention that I'm also not very sure how well DistIL output will play with Mono at its current state, because it hardcodes some assumptions about .NET core being the only/main target, so more work might be needed than just the build pipeline. (for example, the IR type system has some reliance on System.Private.CoreLib, and some opts emit direct access to private List/Span fields.)

Most of the optimizations you have listed don't seem super complicated to implement at first (the devil is always in the details ofc), apart mostly from interprocedural rewrites. I feel like it would be more productive to fix some of them directly instead of implementing opt passes, but that's not a strong opinion and I understand this might involve some bureaucracy and things move slowly lol.

Some minor implementation notes I can think of:

DaZombieKiller commented 1 month ago

I should mention that I'm also not very sure how well DistIL output will play with Mono at its current state, because it hardcodes some assumptions about .NET core being the only/main target, so more work might be needed than just the build pipeline. (for example, the IR type system has some reliance on System.Private.CoreLib, and some opts emit direct access to private List/Span fields.)

Mono's BCL is roughly a 30%/30%/40% split between .NET Framework, .NET Core and custom. List<T> and Span<T> use the .NET Core implementations (for Span<T> it is the ByReference<T> implementation rather than one using ref fields). So I think that should be fine for the most part.

Most of the optimizations you have listed don't seem super complicated to implement at first (the devil is always in the details ofc), apart mostly from interprocedural rewrites. I feel like it would be more productive to fix some of them directly instead of implementing opt passes, but that's not a strong opinion and I understand this might involve some bureaucracy and things move slowly lol.

Yeah, ideally stuff like that would just be fixed within Unity itself. That said, many projects are stuck on specific versions of the engine with no feasible upgrade path, so they wouldn't be able to benefit from it.

Some of the Unsafe intrinsics could be replaced with the corresponding IL instructions directly (maybe apart from As<> and others, which I believe are needed by the JIT for type tracking)

Mono has intrinsic recognition for the Unsafe type, so it may be more beneficial to call directly into the internal implementation available in the BCL. That said, I haven't really verified what the codegen difference is like between Unsafe and the equivalent IL under Mono yet.

I should also note: Mono (specifically talking about legacy Mono & Unity, rather than the newer Mono from dotnet/runtime) does not recognize IgnoresAccessChecksToAttribute. The closest equivalent under Mono would be this:

[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]

Which will give you the same behavior as if you had [IgnoresAccessChecksTo]'d every single assembly.