Skip to content

SILCombine: optimize casts of existential boxes. #33179

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

Merged
merged 2 commits into from
Jul 31, 2020

Conversation

eeckstein
Copy link
Contributor

@eeckstein eeckstein commented Jul 29, 2020

Optimize the unconditional_checked_cast_addr in this pattern:

   %box = alloc_existential_box $Error, $ConcreteError
   %a = project_existential_box $ConcreteError in %b : $Error
   store %value to %a : $*ConcreteError
   %err = alloc_stack $Error
   store %box to %err : $*Error
   %dest = alloc_stack $ConcreteError
   unconditional_checked_cast_addr Error in %err : $*Error to ConcreteError in %dest : $*ConcreteError

to:

   ...
   retain_value %value : $ConcreteError
   destroy_addr %err : $*Error
   store %value to %dest $*ConcreteError

This lets the alloc_existential_box become dead and it can be removed in following optimizations.
The same optimization is also done for conditional_checked_cast_addr.

There is also an implication for debugging:
Each "throw" in the code calls the runtime function swift_willThrow. The function is used by the debugger to set a breakpoint and also add hooks.
This optimization can completely eliminate a "throw", including the runtime call.
So, with optimized code, the user might not see the program to break at a throw, whereas in the source code it is actually throwing.
On the other hand, eliminating the existential box is a significant performance win and we don't guarantee any debugging behavior for optimized code anyway. So I think this is a reasonable trade-off.
I added an option "-Xllvm -keep-will-throw-call" to keep the runtime call which can be used if someone want's to reliably break on "throw" in optimized builds.

To make this optimization work smoothly with the Result type from the library, I had to make the Result methods inlinable. For a use case see the SILOptimizer/existential_box_elimination.swift test file.

rdar://problem/66055678

@eeckstein
Copy link
Contributor Author

@swift-ci test

@eeckstein
Copy link
Contributor Author

@swift-ci benchmark

@eeckstein
Copy link
Contributor Author

@fredriss do you have any concerns regarding the implication for debugging (see my comment in the description)?

@swift-ci
Copy link
Contributor

Performance: -O

Improvement OLD NEW DELTA RATIO
DropFirstArrayLazy 14 13 -7.1% 1.08x (?)

Code size: -O

Performance: -Osize

Improvement OLD NEW DELTA RATIO
FlattenListFlatMap 4225 3797 -10.1% 1.11x (?)
FlattenListLoop 1522 1419 -6.8% 1.07x (?)

Code size: -Osize

Performance: -Onone

Code size: -swiftlibs

How to read the data The tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.

If you see any unexpected regressions, you should consider fixing the
regressions before you merge the PR.

Noise: Sometimes the performance results (not code size!) contain false
alarms. Unexpected regressions which are marked with '(?)' are probably noise.
If you see regressions which you cannot explain you can try to run the
benchmarks again. If regressions still show up, please consult with the
performance team (@eeckstein).

Hardware Overview
  Model Name: Mac mini
  Model Identifier: Macmini8,1
  Processor Name: 6-Core Intel Core i7
  Processor Speed: 3.2 GHz
  Number of Processors: 1
  Total Number of Cores: 6
  L2 Cache (per Core): 256 KB
  L3 Cache: 12 MB
  Memory: 64 GB

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 61d8bbd92dff1a6ee29c5412c911e16782dc64a3

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 61d8bbd92dff1a6ee29c5412c911e16782dc64a3

@eeckstein eeckstein force-pushed the existential-box-elimination branch from 61d8bbd to 303a1bd Compare July 29, 2020 16:36
@eeckstein
Copy link
Contributor Author

@swift-ci test

1 similar comment
@eeckstein
Copy link
Contributor Author

@swift-ci test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 61d8bbd92dff1a6ee29c5412c911e16782dc64a3

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 61d8bbd92dff1a6ee29c5412c911e16782dc64a3

@eeckstein eeckstein force-pushed the existential-box-elimination branch from 303a1bd to 99e96e3 Compare July 29, 2020 19:57
The "Result" type is expected to have no overhead when used.
This requires the member functions to be inlinable.
Which is probably okay for library evolution, because it's unlikely that those methods change in an incompatible way.
 Optimize the unconditional_checked_cast_addr in this pattern:

   %box = alloc_existential_box $Error, $ConcreteError
   %a = project_existential_box $ConcreteError in %b : $Error
   store %value to %a : $*ConcreteError
   %err = alloc_stack $Error
   store %box to %err : $*Error
   %dest = alloc_stack $ConcreteError
   unconditional_checked_cast_addr Error in %err : $*Error to ConcreteError in %dest : $*ConcreteError

to:
   ...
   retain_value %value : $ConcreteError
   destroy_addr %err : $*Error
   store %value to %dest $*ConcreteError

This lets the alloc_existential_box become dead and it can be removed in following optimizations.
The same optimization is also done for conditional_checked_cast_addr.

There is also an implication for debugging:
Each "throw" in the code calls the runtime function swift_willThrow. The function is used by the debugger to set a breakpoint and also add hooks.
This optimization can completely eliminate a "throw", including the runtime call.
So, with optimized code, the user might not see the program to break at a throw, whereas in the source code it is actually throwing.
On the other hand, eliminating the existential box is a significant performance win and we don't guarantee any debugging behavior for optimized code anyway. So I think this is a reasonable trade-off.
I added an option "-Xllvm -keep-will-throw-call" to keep the runtime call which can be used if someone want's to reliably break on "throw" in optimized builds.

rdar://problem/66055678
@eeckstein eeckstein force-pushed the existential-box-elimination branch from 99e96e3 to 662f03e Compare July 29, 2020 19:57
@eeckstein
Copy link
Contributor Author

@swift-ci test

1 similar comment
@eeckstein
Copy link
Contributor Author

@swift-ci test

@eeckstein eeckstein requested a review from mikeash July 29, 2020 20:06
@eeckstein eeckstein merged commit 53d3dda into swiftlang:master Jul 31, 2020
@eeckstein eeckstein deleted the existential-box-elimination branch July 31, 2020 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants