-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
unify ThrowHelper methods #60703
Comments
This doesn't seem very valuable to me. Is there really a lot that's duplicated? In general each library using this pattern treats it as very fluid and adds only what and exactly what it needs. Devs would now need to think about how that impacts other libraries, and it doesn't seem like there'd be a lot of gain. I would, however, love to see the need for ThrowHelper go away entirely via IL codegen / an optimization pass. |
I don't recall seeing one, but I didn't check :-) This probably affects inlining as well as other optimizations. cc @AndyAyersMS |
One route to eliminating the need for throw helpers is to revive the work I did for partial jitting (eg #34522). But we'd also want it to work for optimized code, which is quite a bit more complicated. I used a prototype of this to project that we could save about ~2% of SPC R2R code size this way. |
I think the best solution for the ThrowHelper problem would be an IL optimization pass at build time that creates them in more compact and optimal shape than humans ever would. .NET Native for UWP had an optimization pass like that. |
🤷♀️ I don't feel strongly.
Do we have tooling for this? That would also fix up symbols for debugging? Aiming for some generality to any code it would perhaps do something like
something like that? For fun, I looked at the IL and asm for these (likely counting wrong) void F(int i) => throw new ArgumentException(nameof(i), ArgumentNull_Key); // 16 bytes IL -> 104 bytes x64
void G(int i) => ThrowArgumentException(nameof(i), ArgumentNull_Key); // 17 bytes IL -> 43 bytes x64
void H(int i) => ThrowArgumentException(ExceptionArgument.obj, ExceptionResource.ArgumentNull_Key); // 9 bytes IL -> 27 bytes x64, I don't know whether changes would be needed on the codegen side, e.g, to know that these helper methods represent cold code, as unconditional throws. |
We do not have a place to add optimizations like this today. Roslyn and IL trimmer do not want to do optimizations. AOT/R2R compiler has platform specific optimizer that does not rewrite IL. So it would need to be a new step or tool in the build toolchain. Maybe we should start with a simple single-purpose tool based on System.Reflection.Metadata as an experiment and see where it gets us. Yes, fixing up debug info takes some care, but it has been done before.
Unclear whether enum is the most compact format to use. Each method and each enum value come with extra metadata that also count towards the binary size and memory footprint. The metadata footprint is typically more than code size for small methods. It is one of the reasons why handwriting it in most compact format is hard. |
If experiments prove value perhaps Roslyn would consider an extension point for IL optimization at their emitter stage, especially if that point made debug symbols fix ups natural. Others might use such a mechanism. |
Cc @jaredpar |
@danmoseley FYI, there's been a lot of talk about that kind of thing over here: dotnet/roslyn#15929 |
Thanks @Joe4evr. Seems discussion there migrated to it being done on the linker. Is IL optimization considered in scope for the linker today? Yet another option is an extension of source generators that can override code. I see a link from that discussion to a proposal for "supersede". I guess I do not know whether LDM will ever support that. |
What would the structure of such IL look like?
The bar for this type of feature is extremely high for the same reason we restrict source generators to adding code only, not modifying it. The moment we allow extension points for modifying existing code then one of the largest value props of C# goes out the window: the ability to have a strongly spec'd language with specified behavior and rules. Arbitrary IL rewriting is essentially "everything becomes undefined" behavior land.
You seemed to have mis-spelled "will" as "might" and "abuse" as "use" 😄 |
Something like this to start with: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Linq/src/System/Linq/ThrowHelper.cs
|
Closing this as it stalled without reaching agreement that it would be worthwhile. |
between them there is plenty of duplication, eg
We should reduce the amount of duplicated code here by at least pulling the common stuff out into a shared file.
Incidentally @BruceForstall do we have an issue for codegen to long term allow us to eliminate throw helpers? I can't find it.
The text was updated successfully, but these errors were encountered: