[WIP, don’t merge] IR pass: Simplify loop forms into a single, simple form#146
[WIP, don’t merge] IR pass: Simplify loop forms into a single, simple form#146matthewhammer wants to merge 46 commits intomasterfrom
Conversation
|
If the rewrites are simple enough (for example, if they don’t need to do complex rewriting of subexpressions), they can just happen in |
|
I wondered about that. Why include them in the IR at all if the goal is to take them out at some point in the pipeline? One reason could be that this makes the transformation optional, though that reason doesn't sound very compelling (why do this at all, then?). @crusso ? |
|
In any case, if my assumptions about what forms to rewrite, and how to rewrite them, are all correct, it will be easy to move this into desugar once I get it right. |
|
I think the guideline of whether you do it in the desugarer (like
|
…ould be straightforward, given answers to the questions
OK, thanks for this. I've put some questions, and a sketch of each planned transform, here: Please have a look, especially for the questions there. |
|
I'm going to continue working this today, following @crusso's tips and advice, just above. |
The benefit is that many passes no longer need to keep a `con_env` around, and many operations in `Type` are now always available, in partiuclar `Type.as_*_sub`, `promote` etc. The downside is that, because of the multi-pass approach in `typing.ml`, the annotation needs to be a `kind ref`. I tried using a `promise`, but it seems that certain things are run more than once in `typing` (the same has caused issues with recoverable error messages before). I am not sure if this is avoidable. If maybe the initialization can be made a bit cleaner in `typing`, then I think this will improve the life of those working on the `Ir` noticably.
the `kind` is already in the `con`, so no need having it here.
so that the choice whether it is a `promise`, or a `ref`, or something else, private to `type.ml`. I tried to make it more promise-like, by trapping when the kind is set a second time to something different, but it seems that `k = k'` can loop.
with a different value. A bit like a promise, but more compatible with `typing.ml` running `infer_dec_typdecs` multiple times. Not sure if it is worth the bother, though.
In `typing.ml`, we replace it with a `con_set`, so that we can still call `avoid`. In `check_ir.ml` we simply drop the field.
it is always the range of `typ_env` anyways. This simplifies a few more functions.
| (* XXX: not sure how to get type info for `next` function... | ||
| - ...how to do I supply a non-empty con_env for `as_obj_sub` ? | ||
| *) | ||
| let _, tfs = Type.as_obj_sub "next" ty1 in |
There was a problem hiding this comment.
I think there actually is a named constant for "next" (Construct.nextN), best to use it here and elsewhere
There was a problem hiding this comment.
Actually, couldn't you just construct the type of next as " () -> ?(E.typ pat)", (without needing an env) but maybe that's cheating.
There was a problem hiding this comment.
@crusso hmm; I cannot use Construct.nextN here, since the code I want to call next (part of the Type module) expects a string, not a name.
There was a problem hiding this comment.
You should be able to get the string from the name. Name is just a single constructor type (or at least it was) to prevent accidental confusion between alpha-convertible identifiers and rigid field names in the syntax. Andreas may have renamed Name to Lab(el) recently (but perhaps only in source). Type's probably just use string for field names...
| let loopWhileE exp1 exp2 = | ||
| (* loop e1 while e2 | ||
| ~~> label l: loop { | ||
| let () = e1 ; |
There was a problem hiding this comment.
Huh? I don't understand this comment; is something wrong in the (source) comment above?
src/construct.ml
Outdated
| blockE [ | ||
| expD exp1 ; | ||
| letD id2 exp2 ; | ||
| expD (ifE id2 |
There was a problem hiding this comment.
why bind to id2? (Perhaps Joachim spotted this one)
There was a problem hiding this comment.
In general, I was using ANF to make things "easier" for me;
yesterday, @nomeata mentioned that this was suboptimal for the WASM output, so I'll avoid these intermediate bindings, and will fix this one.
There was a problem hiding this comment.
K, but unfortunately, there's a cost, especially with recursion story (though I suspect Joachim optimizes this away now)
| val labelE : Syntax.id -> typ -> exp -> exp | ||
| val loopE: exp -> exp option -> exp | ||
|
|
||
| val whileE' : exp -> exp -> exp' |
There was a problem hiding this comment.
Could these be whileE : ... -> exp etc, dropping the primes, in keeping with the rest of the API?
There was a problem hiding this comment.
You'll need to correctly construct the type (of course) but also the effect too or this will mess up other transformations.
There was a problem hiding this comment.
I don't see how to do this "cleanly"; how would you adjust these use cases? (see Simploop):
(* Transformation from loop-while to loop: *)
| I.LoopE (e1, Some e2) -> Construct.loopWhileE' (exp e1) (exp e2)
(* Transformation from while to loop: *)
| I.WhileE (e1, e2) -> Construct.whileE' (exp e1) (exp e2)
(* Transformation from for to loop: *)
| I.ForE (p, e1, e2) -> Construct.forE' p (exp e1) (exp e2)
There was a problem hiding this comment.
True, but I guess here you could just do loopWhile (exp e1) (exp e2).it. I'm pretty sure I do that elsewhere in a few places...
There was a problem hiding this comment.
Yes, that's the pattern I was trying to avoid, but I can use it if you have a strong preference.
I don't have a preference myself, I just thought the version projecting out the it seemed slightly more "messy"; but again, I have no strong opinion; I will fix this.
There was a problem hiding this comment.
I guess you could provide both in this case... but, in general, the construct functions are there to make it easier to construct (nested) expressions, where its more convenient to have the exp return type for composing subexpressions.
There was a problem hiding this comment.
how would you adjust these use cases?
You would have to refactor the surrounding code to expect an exp, not an exp'. But that might be ugly, so maybe having both is fine.
| - Continue isn't special IRCC, it's just a special named label ("continue-" or something like that) introduced by the parser. | ||
| (the loop(_,None) construct just loops forever, but you can still exit via break). | ||
|
|
||
| - I would indeed just do this desugaring in the parser |
There was a problem hiding this comment.
Not sure about this comment - I think our intention is to eventually remove the complicated loops from the IR and desugar source loops to ir loops directly in the translator...
I guess once could imagine doing forE loops slight differently, optimizing for each collection type, so perhaps those would be best as a full blown transformation....
Desugaring is actually a bad name for the source to IR translation... desugaring usually means source 2 source...
There was a problem hiding this comment.
Even if we don't remove the constructs in the src->ir desugaring, it would be great if we could move the transform up the pipeline so awaitopt.ml and async.ml and tailcall.ml no longer need to consider all those looping constructs. At the moment only the compiler benefits.
There was a problem hiding this comment.
I would indeed just do this desugaring in the parser
you (@crusso) wrote that, not me; I pasted it here from a GH conversation we had earlier :)
are you saying that you no longer believe this?
The rest of your comments make sense (e.g., regarding moving up this translation in the pipeline).
There was a problem hiding this comment.
Here's a link to the source of the aforementioned quoted text:
#146 (comment)
There was a problem hiding this comment.
Ignore my comment - I think Andreas and Joachim didn't want this for better error reporting (and I guess to keep the AST more in line with syntax).
|
Is it time to un-WIP this and get it merged? Do you want to include it in |
I think: Yes, and yes. I can work getting this done today. |
|
@nomeata in preparation for doing this today, I moved the transforms into
|
For those too lazy to search for this comment: If you inline
Well, if there are conflicts, you have to resolve them. Why do you think they are wrong? Or is the problem that you have to do them repeatedly, as you rebase your patch series? You don’t really have a “clean, narrative” patch set anyways, so rebasing does not seem to be the right thing. Simply
Master is green, so these failures are introduced by your change. Looking at the output, it seem that the IR output has changed in cases where the compiler dumps IR (variable names have changed). If the changes are expected, then run |
|
BTW, I just checked what happens if But actually the typing rule for I don’t think anything is wrong with |
|
Thanks for all of the feedback and clarifications @nomeata.
Yep, I did that naively this morning. It was too early. I think that was the issue. Thanks for your investigation, in any case; I'll leave that transform for
If I understand correctly, then "yes", each time I want to stay in sync with
Yes, I do see one instance of that. However, I also see things like this as well: This doesn't seem like a simple variable name difference. (am I missing something?). There are several errors like this, with the same In the meantime, I'm going to try to update DVM to see if that helps. |
|
Updates:
|
|
The commit history here is a mess, so I made a cleaner PR with a distillation of my changes. We can close this PR. |
If you feel like it, we can video-chat about the meaning and implications of rebase vs. merge. More efficient than writing about it here. |
## Changelog for ic-hs: Branch: master Commits: [dfinity/ic-hs@927d8152...6ddff2e4](dfinity/ic-hs@927d815...6ddff2e) * [`54dcdd0c`](dfinity/ic-hs@54dcdd0) .github/workflows/release.yml: only sync github pages on linux ([dfinity/ic-hs#143](https://github.com/dfinity/ic-hs/issues/143)) * [`81ac2e9c`](dfinity/ic-hs@81ac2e9) .github/workflows/release.yml: fix github-pages-deploy-action version ([dfinity/ic-hs#144](https://github.com/dfinity/ic-hs/issues/144)) * [`09a7b445`](dfinity/ic-hs@09a7b44) fix broken link in README ([dfinity/ic-hs#145](https://github.com/dfinity/ic-hs/issues/145)) * [`b393d8cb`](dfinity/ic-hs@b393d8c) .github/workflows/release.yml: skip coverage job on darwin ([dfinity/ic-hs#146](https://github.com/dfinity/ic-hs/issues/146)) * [`55870466`](dfinity/ic-hs@5587046) split haskell modules to reduce memory usage ([dfinity/ic-hs#142](https://github.com/dfinity/ic-hs/issues/142)) * [`6ddff2e4`](dfinity/ic-hs@6ddff2e) add httpbin implementation in Rust, bump nixpkgs, and sync universal_canister with ic monorepo ([dfinity/ic-hs#138](https://github.com/dfinity/ic-hs/issues/138))
## Changelog for ic-hs: Branch: master Commits: [dfinity/ic-hs@927d8152...808ba08b](dfinity/ic-hs@927d815...808ba08) * [`54dcdd0c`](dfinity/ic-hs@54dcdd0) .github/workflows/release.yml: only sync github pages on linux ([dfinity/ic-hs#143](https://github.com/dfinity/ic-hs/issues/143)) * [`81ac2e9c`](dfinity/ic-hs@81ac2e9) .github/workflows/release.yml: fix github-pages-deploy-action version ([dfinity/ic-hs#144](https://github.com/dfinity/ic-hs/issues/144)) * [`09a7b445`](dfinity/ic-hs@09a7b44) fix broken link in README ([dfinity/ic-hs#145](https://github.com/dfinity/ic-hs/issues/145)) * [`b393d8cb`](dfinity/ic-hs@b393d8c) .github/workflows/release.yml: skip coverage job on darwin ([dfinity/ic-hs#146](https://github.com/dfinity/ic-hs/issues/146)) * [`55870466`](dfinity/ic-hs@5587046) split haskell modules to reduce memory usage ([dfinity/ic-hs#142](https://github.com/dfinity/ic-hs/issues/142)) * [`6ddff2e4`](dfinity/ic-hs@6ddff2e) add httpbin implementation in Rust, bump nixpkgs, and sync universal_canister with ic monorepo ([dfinity/ic-hs#138](https://github.com/dfinity/ic-hs/issues/138)) * [`808ba08b`](dfinity/ic-hs@808ba08) split Multiple controllers tests ([dfinity/ic-hs#149](https://github.com/dfinity/ic-hs/issues/149))
## Changelog for ic-hs: Branch: master Commits: [dfinity/ic-hs@927d8152...bdaaa321](dfinity/ic-hs@927d815...bdaaa32) * [`54dcdd0c`](dfinity/ic-hs@54dcdd0) .github/workflows/release.yml: only sync github pages on linux ([dfinity/ic-hs#143](https://github.com/dfinity/ic-hs/issues/143)) * [`81ac2e9c`](dfinity/ic-hs@81ac2e9) .github/workflows/release.yml: fix github-pages-deploy-action version ([dfinity/ic-hs#144](https://github.com/dfinity/ic-hs/issues/144)) * [`09a7b445`](dfinity/ic-hs@09a7b44) fix broken link in README ([dfinity/ic-hs#145](https://github.com/dfinity/ic-hs/issues/145)) * [`b393d8cb`](dfinity/ic-hs@b393d8c) .github/workflows/release.yml: skip coverage job on darwin ([dfinity/ic-hs#146](https://github.com/dfinity/ic-hs/issues/146)) * [`55870466`](dfinity/ic-hs@5587046) split haskell modules to reduce memory usage ([dfinity/ic-hs#142](https://github.com/dfinity/ic-hs/issues/142)) * [`6ddff2e4`](dfinity/ic-hs@6ddff2e) add httpbin implementation in Rust, bump nixpkgs, and sync universal_canister with ic monorepo ([dfinity/ic-hs#138](https://github.com/dfinity/ic-hs/issues/138)) * [`808ba08b`](dfinity/ic-hs@808ba08) split Multiple controllers tests ([dfinity/ic-hs#149](https://github.com/dfinity/ic-hs/issues/149)) * [`bdaaa321`](dfinity/ic-hs@bdaaa32) bump cachix/install-nix-action to v20 ([dfinity/ic-hs#150](https://github.com/dfinity/ic-hs/issues/150))
## Changelog for ic-hs: Branch: master Commits: [dfinity/ic-hs@927d8152...bdaaa321](dfinity/ic-hs@927d815...bdaaa32) * [`54dcdd0c`](dfinity/ic-hs@54dcdd0) .github/workflows/release.yml: only sync github pages on linux ([dfinity/ic-hs#143](https://github.com/dfinity/ic-hs/issues/143)) * [`81ac2e9c`](dfinity/ic-hs@81ac2e9) .github/workflows/release.yml: fix github-pages-deploy-action version ([dfinity/ic-hs#144](https://github.com/dfinity/ic-hs/issues/144)) * [`09a7b445`](dfinity/ic-hs@09a7b44) fix broken link in README ([dfinity/ic-hs#145](https://github.com/dfinity/ic-hs/issues/145)) * [`b393d8cb`](dfinity/ic-hs@b393d8c) .github/workflows/release.yml: skip coverage job on darwin ([dfinity/ic-hs#146](https://github.com/dfinity/ic-hs/issues/146)) * [`55870466`](dfinity/ic-hs@5587046) split haskell modules to reduce memory usage ([dfinity/ic-hs#142](https://github.com/dfinity/ic-hs/issues/142)) * [`6ddff2e4`](dfinity/ic-hs@6ddff2e) add httpbin implementation in Rust, bump nixpkgs, and sync universal_canister with ic monorepo ([dfinity/ic-hs#138](https://github.com/dfinity/ic-hs/issues/138)) * [`808ba08b`](dfinity/ic-hs@808ba08) split Multiple controllers tests ([dfinity/ic-hs#149](https://github.com/dfinity/ic-hs/issues/149)) * [`bdaaa321`](dfinity/ic-hs@bdaaa32) bump cachix/install-nix-action to v20 ([dfinity/ic-hs#150](https://github.com/dfinity/ic-hs/issues/150))
Initially, this pass is the identify function, and this description, to make sure I'm heading the right direction. The three transformation cases mentioned below should each be fairly straightforward, given this set up.
Loop transformations
To make the IR "narrower", this pass rewrites the following forms each to
LoopE(e', None), for some appropriatee':LoopE(_, Some _)WhileE(_, _)ForE(_,_,_)To afford the control flow of these special forms, the new loop body
e'introduces labels andBreakEs, as needed.