-
-
Notifications
You must be signed in to change notification settings - Fork 47
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
Inlining of methods #343
Comments
To add some context: LLVM doesn't optimize across modules unless you enable link time optimizations. LTO can drastically increase compile times, and doesn't always produce better results. Because of this, if you call an Inko method defined in module As a test I hacked together some dirty changes that basically link all LLVM modules together and compile them as a whole, completely ignoring incremental compilation in the process. For https://github.com/jinyus/related_post_gen this results in a 5x improvement of runtime performance when using My long-term desire is to perform enough inlining and optimizations at the MIR level, such that we can then pass that to LLVM to take care of platform specific optimizations. In such a case it doesn't really matter that LLVM only inlines on a per module basis, as we've already done most of the work (and ideally in parallel as much as possible). This would also benefit any future additional backends, should we ever add those. |
For 0.17.0 we'll start by adding support for
The Inlining is done at the MIR level, allowing us to inline across modules. Inlining is performed after type specialization. When inlining we need to rewrite register IDs. To do so, we take the maximum register ID of the method we're inlining into and note it down as |
Note to self: if a method is tagged as |
For inlining we need to take care to flush caches accordingly. For example, if module Since multiple methods from One option is this: When inlining In addition, we flag |
The compiler is now able to inline calls to static and instance methods. For each method we calculate a rough weight/cost, and calls are inlined into their callers until the maximum weight is reached. Inlining is done bottom-up using Tarjan's strongly connected components algorithm, reducing the amount of duplicate inlining work. Inlining is also done in a deterministic order as to ensure incremental compilation caches can be reused as much as possible. The current inlining threshold is on the conservative end as to not increase compile times and compile-time memory usage too much. Over time we may relax this based on user reports and any extra optimization passes we might add. If a method is annotated with the `inline` keyword, it's _always_ inlined into the caller regardless of it or the caller's weight. This is meant to be used when you want to guarantee a method is inlined, such as for the various operator methods of Int, Float, Bool, etc. Because of this guarantee one should use it sparingly, as to not increase the compile time and executable size too much. This fixes #343. Changelog: added
The compiler is now able to inline calls to static and instance methods. For each method we calculate a rough weight/cost, and calls are inlined into their callers until the maximum weight is reached. Inlining is done bottom-up using Tarjan's strongly connected components algorithm, reducing the amount of duplicate inlining work. Inlining is also done in a deterministic order as to ensure incremental compilation caches can be reused as much as possible. The current inlining threshold is on the conservative end as to not increase compile times and compile-time memory usage too much. Over time we may relax this based on user reports and any extra optimization passes we might add. If a method is annotated with the `inline` keyword, it's _always_ inlined into the caller regardless of it or the caller's weight. This is meant to be used when you want to guarantee a method is inlined, such as for the various operator methods of Int, Float, Bool, etc. Because of this guarantee one should use it sparingly, as to not increase the compile time and executable size too much. This fixes #343. Changelog: added
The compiler is now able to inline calls to static and instance methods. For each method we calculate a rough weight/cost, and calls are inlined into their callers until the maximum weight is reached. Inlining is done bottom-up using Tarjan's strongly connected components algorithm, reducing the amount of duplicate inlining work. Inlining is also done in a deterministic order as to ensure incremental compilation caches can be reused as much as possible. The current inlining threshold is on the conservative end as to not increase compile times and compile-time memory usage too much. Over time we may relax this based on user reports and any extra optimization passes we might add. If a method is annotated with the `inline` keyword, it's _always_ inlined into the caller regardless of it or the caller's weight. This is meant to be used when you want to guarantee a method is inlined, such as for the various operator methods of Int, Float, Bool, etc. Because of this guarantee one should use it sparingly, as to not increase the compile time and executable size too much. This fixes #343. Changelog: added
The compiler is now able to inline calls to static and instance methods. For each method we calculate a rough weight/cost, and calls are inlined into their callers until the maximum weight is reached. Inlining is done bottom-up using Tarjan's strongly connected components algorithm, reducing the amount of duplicate inlining work. Inlining is also done in a deterministic order as to ensure incremental compilation caches can be reused as much as possible. The current inlining threshold is on the conservative end as to not increase compile times and compile-time memory usage too much. Over time we may relax this based on user reports and any extra optimization passes we might add. If a method is annotated with the `inline` keyword, it's _always_ inlined into the caller regardless of it or the caller's weight. This is meant to be used when you want to guarantee a method is inlined, such as for the various operator methods of Int, Float, Bool, etc. Because of this guarantee one should use it sparingly, as to not increase the compile time and executable size too much. This fixes #343. Changelog: added
The compiler is now able to inline calls to static and instance methods. For each method we calculate a rough weight/cost, and calls are inlined into their callers until the maximum weight is reached. Inlining is done bottom-up using Tarjan's strongly connected components algorithm, reducing the amount of duplicate inlining work. Inlining is also done in a deterministic order as to ensure incremental compilation caches can be reused as much as possible. The current inlining threshold is on the conservative end as to not increase compile times and compile-time memory usage too much. Over time we may relax this based on user reports and any extra optimization passes we might add. If a method is annotated with the `inline` keyword, it's _always_ inlined into the caller regardless of it or the caller's weight. This is meant to be used when you want to guarantee a method is inlined, such as for the various operator methods of Int, Float, Bool, etc. Because of this guarantee one should use it sparingly, as to not increase the compile time and executable size too much. This fixes #343. Changelog: added
The compiler is now able to inline calls to static and instance methods. For each method we calculate a rough weight/cost, and calls are inlined into their callers until the maximum weight is reached. Inlining is done bottom-up using Tarjan's strongly connected components algorithm, reducing the amount of duplicate inlining work. Inlining is also done in a deterministic order as to ensure incremental compilation caches can be reused as much as possible. The current inlining threshold is on the conservative end as to not increase compile times and compile-time memory usage too much. Over time we may relax this based on user reports and any extra optimization passes we might add. If a method is annotated with the `inline` keyword, it's _always_ inlined into the caller regardless of it or the caller's weight. This is meant to be used when you want to guarantee a method is inlined, such as for the various operator methods of Int, Float, Bool, etc. Because of this guarantee one should use it sparingly, as to not increase the compile time and executable size too much. This fixes #343. Changelog: added
Inlining is probably the single most important optimisation pass to apply, as many other optimisations depend on it. Inko's bytecode compiler doesn't perform inlining at the moment, and we should change that.
The first step towards this is to determine what criteria we'll use to inline a method, and what other languages (e.g. Swift and Rust) use. Simply looking at the number of lines of code probably isn't good enough.
async
methods would never be inlined because we can't, they're used for messages after all.When inlining methods, care should be taken to "rewrite" whatever
self
refers to in the inlined code. That is, if we inlineA.foo
intoB.bar
, any reference toself
inA.foo
should point to the appropriate instance ofA
, notB
.TODO
inline
keyword if the method should always be inlined--opt=none
inline
methods that can't be called through dynamic dispatch: Inlining of methods #343 (comment)std.debug.stacktrace
is used correctly in the face of inlined code (e.g. currently it breaks due to it not expecting inlined frames)True
andFalse
instructions intoBool
Resources
The text was updated successfully, but these errors were encountered: