-
Notifications
You must be signed in to change notification settings - Fork 31
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
Make local continuations direct style if possible #777
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
jiribenes
reviewed
Jan 14, 2025
effekt/shared/src/main/scala/effekt/core/optimizer/Normalizer.scala
Outdated
Show resolved
Hide resolved
At some point, we could clean this up, add a separate IR, and / or document the transformation formally somewhere (the earlier the better, since I will forget the details). |
EveEme
pushed a commit
to EveEme/effekt
that referenced
this pull request
Jan 20, 2025
This PR tries to remove the overhead of CPS (and trampolining) for direct style code. Essentially, for the following code in CPS ``` let cont k(x, ks) = ... if (cond) { k(42, ks') } else { k(43, ks') } ``` we are now generating the following JS: ```javascript let x; if (cond) { x = 42 } else { x = 43 }; ... ``` This only works under the assumption that: - `k` is not used somewhere in a more first-class manner (under a different context, as part of a continuation passed to a function, etc.) - `k` is always called with the currently in scope meta continuation `ks'` (so that we can avoid assigning it -- this turns out to be essential with the feature interaction of loops) The interaction with loops was difficult to get right: - turning recursive functions into direct style loops makes additional continuations more local - making a continuation more local turns more functions into loops As a result, the function `parse_worker` (from the `parsing_dollars` benchmark) is now a tight loop. **Before the PR** ```javascript function parse_worker_0(a_1, ks_7, k_4) { const x_0 = i_0.value; function k_3(c_0, ks_4) { if (c_0 === (36)) { return () => parse_worker_0((a_1 + (1)), ks_4, k_4); } else if (c_0 === (10)) { const x_1 = s_0.value; s_0.value = (x_1 + a_1); return () => parse_worker_0(0, ks_4, k_4); } else { return SHIFT(p_0, (k_5, ks_5, k_6) => k_6($effekt.unit, ks_5), ks_4, k_4); } } if ((x_0 > n_0)) { return SHIFT(p_0, (k_7, ks_6, k_8) => k_8($effekt.unit, ks_6), ks_7, (v_r_1, ks_8) => $effekt.emptyMatch()); } else { const x_2 = j_0.value; if (x_2 === (0)) { const x_3 = i_0.value; i_0.value = (x_3 + (1)); const x_4 = i_0.value; j_0.value = x_4; return () => k_3(10, ks_7); } else { const x_5 = j_0.value; j_0.value = (x_5 - (1)); return () => k_3(36, ks_7); } } } ``` **After the PR** ```javascript function parse_worker_0(a_9, ks_293, k_236) { parse_worker_0: while (true) { const x_11 = i_13.value; let c_0 = undefined; if ((x_11 > n_17)) { return SHIFT(p_32, (k_232, ks_292, k_233) => k_233($effekt.unit, ks_292), ks_293, (v_r_95, ks_294) => $effekt.emptyMatch()); } else { const x_12 = j_0.value; if (x_12 === (0)) { const x_13 = i_13.value; i_13.value = (x_13 + (1)); const x_14 = i_13.value; j_0.value = x_14; c_0 = 10; } else { const x_15 = j_0.value; j_0.value = (x_15 - (1)); c_0 = 36; } } if (c_0 === (36)) { /* prepare tail call */ const a_8 = (a_9 + (1)); a_9 = a_8; continue parse_worker_0; } else if (c_0 === (10)) { const x_16 = s_4.value; s_4.value = (x_16 + a_9); /* prepare tail call */ const a_10 = 0; a_9 = a_10; continue parse_worker_0; } else { return SHIFT(p_32, (k_234, ks_295, k_235) => k_235($effekt.unit, ks_295), ks_293, k_236); } } } ``` Additionally, here are a few preliminary benchmark results: ![image](https://github.com/user-attachments/assets/6d3bd21a-fdcf-4149-8e0a-78ba48c4c6d2)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR tries to remove the overhead of CPS (and trampolining) for direct style code.
Essentially, for the following code in CPS
we are now generating the following JS:
This only works under the assumption that:
k
is not used somewhere in a more first-class manner (under a different context, as part of a continuation passed to a function, etc.)k
is always called with the currently in scope meta continuationks'
(so that we can avoid assigning it -- this turns out to be essential with the feature interaction of loops)The interaction with loops was difficult to get right:
As a result, the function
parse_worker
(from theparsing_dollars
benchmark) is now a tight loop.Before the PR
After the PR
Additionally, here are a few preliminary benchmark results: