-
Notifications
You must be signed in to change notification settings - Fork 128
Add constant propagation through methods with parameters or complex bodies #2556
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
Conversation
|
I'm not actually reviewing this, but I had a couple areas of interest that I skimmed:
|
Do you have an example why? I didn't understand the comment.
The code is not used for actually inlining only for result evaluation.
I had logic there but removed, now thinking about it again it needs more work even for other scenarios.
It does not but there is a bug in the logic (missing a special case for static context). |
It has to do with CompilationRelaxations.NoStringInterning (that the C# compiler automatically emits into all assemblies it produces). The docs on it are pretty poor, here's StackOverflow: https://stackoverflow.com/questions/15601916/how-does-compilationrelaxations-nostringinterning-actually-work. That said, I looked into the implementation on the runtime side and at this point (after summer of last year) it seems like we no longer need to worry about it. I created dotnet/runtime#64521 to really double check it's no longer a concern. I expect JanK to scream if it's not the case and I'm wrong. |
|
|
||
| if (left is string sleft && right is string sright) { | ||
| if (method.Name.Length == 6) | ||
| return Instruction.Create (OpCodes.Ldstr, string.Concat (sleft, sright)); |
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 can create a very long string if called recursively.
Just curious - what are examples of CoreLib methods where this helps with trimming?
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 does not matter if it creates long string as this logic is used for result computation only (value is not inlined). I found it for example here https://cs.github.com/dotnet/runtime/blob/f85ea976f81945ea18cd5dc71959cccecdc93cd2/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs?q=GetUtcFullDisplayName#L2111
…dies This change also - Adds simple inlining of method return values. - Fixes several shortcomings of existing implementation like ignoring methods called from unreachable blocks. This helps to reduce the size of all runtime assemblies. For example size of SPC for the default iOS project is reduced by 4% mostly due to metadata removal. The linker speed improves a little bit (~3%) for the same project.
2ebe241 to
c59b3a0
Compare
There is extra logic to take care of that.
Yep, made the check more comprehensive. |
| sizeOfImpl = (IntPtrSize ??= FindSizeMethod (_context.TryResolve (operand))); | ||
| } | ||
| var cpl = new CalleePayload (md, GetArgumentsOnStack (md, body.Instructions, i)); | ||
| MethodResult? call_result = optimizer.TryGetMethodCallResult (cpl, optimizer.GetRecursionGuard ()); |
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 is a major difference in how the implementation worked before - the previous version didn't have on-the-stack recursion, it handled this by storing info on the heap. I don't remember if we ever measured the maximum depth reached, but in the previous version it would not matter all. In this version the linker could run out of stack space and hard crash.
It took some non-trivial logic to make the code work well with on-the-heap storage, so the on-the-stack version is simpler. It could be OK this way, but we should be aware of it.
At the very least we should add a hard coded constant and cap the stack depth - basically if we run out of stack linker would not crash, but it would not optimize as well as it could. Much better than hard crashing.
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.
Added the deep stack check, the implementation goes only up to 3 iterations down on the default iOS app so I think this is fine.
| } | ||
| } | ||
|
|
||
| processor.Replace (i, result.GetPrototype ()); |
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 must admit I find the name GetPrototype pretty confusing - it's a copy (or shallow copy), why not call it that?
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 can be both, shallow copy for complex instruction, deep copy for simple instruction. I have upstream PR which I'll merge here once approved (including the name)
|
|
||
| [MemberNotNullWhen (true, "Result")] | ||
| public bool Analyze () | ||
| public bool Analyze (in CalleePayload callee, Stack<MethodDefinition> callStack) |
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 is very similar to MethodBodyScanner - might be worth looking into potential sharing. Or at the very least comparing the two implementations.
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.
That said - with the linker switching over to the "solver based data flow", the method body scanner will probably change quite a bit - especially around branching and such (basically it will become BasicBlockScanner instead).
vitek-karas
left a comment
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 this looks good now.
…odies (dotnet/linker#2556) Commit migrated from dotnet/linker@bacafd1
This change also
methods called from unreachable blocks.
This helps to reduce the size of all runtime assemblies. For example size
of SPC for the default iOS project is reduced by 4% mostly due to metadata
removal. The linker speed improves a little bit (~3%) for the same
project.