Skip to content
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

PLT-871: ThunkRecursions conservative mode #5560

Merged
merged 1 commit into from Sep 27, 2023
Merged

Conversation

bezirg
Copy link
Contributor

@bezirg bezirg commented Sep 25, 2023

The current thunk recursions transformation has 2 problems:

  1. does not preserve order of effects, so it may rearrange an error or a trace
  2. does not respect the original strictness of some pure-strict bindings, turning them into lazy (not extremely bad)

This PR fixes (1) and (2)

Pre-submit checklist:

  • Branch
    • Tests are provided (if possible)
    • Commit sequence broadly makes sense
    • Key commits have useful messages
    • Changelog fragments have been written (if appropriate)
    • Relevant tickets are mentioned in commit messages
    • Formatting, PNG optimization, etc. are updated
  • PR
    • (For external contributions) Corresponding issue exists and is linked in the description
    • Targeting master unless this is a cherry-pick backport
    • Self-reviewed the diff
    • Useful pull request description
    • Reviewer requested

@bezirg bezirg force-pushed the bezirg/fix-thunkrecursions branch 3 times, most recently from fcba41d to 5085623 Compare September 25, 2023 14:57
Copy link
Contributor

@michaelpj michaelpj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change definitely makes sense. Various thoughts...

At the moment we only do this for bindings whose RHS is potentially impure, although in
principle it could be an improvement in other cases because it would allow using the faster
strict binding in the body. Unclear.
Using the default optimization settings, (a) the order of effects may change and (b)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see some discussion of why, maybe with an example!

let (toNonStrictify, rest) = NE.partition needsNonStrictify bs
-- MAYBE: use some prism/traversal to keep the original arrangement of the let group
-- this is not a semantic problem, but just a stylistic/debugging issue where the original
-- let-group will have reordered the (lazified) bindings
Copy link
Contributor

@michaelpj michaelpj Sep 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One way you could maybe do this is to avoid the partitioning and do more stuff unconditionally. Something like:

strictifyIfNecessary ::Binding -> Binding
strictifyIfNecessary b | needsNonStrictify b = ...
strictifyIfNecessary b = b

...
   strictifiedBindings = fmap strictifyIfNecessary bs
   -- or I guess you could again put the needsNonStrictify condition here
   strictifiers = fmap mkStrictifierB bs

I think we can add the strictifying bindings unconditionally: if the RHS is a strict variable (which is the only case in which we currently omit them), then the inliner should remove them.

e.g.

let rec
   !f = /\ a ...
   !x = \y ....
in t
--->
let rec
   ~f = /\ a ....
   !x = \y ...
in 
let 
   !f = f
   !x =x
in t

The inliner should deal with the x=x binding in this case. I actually think this sort of thing can be quite nice: you emit worse code but rely on the simplifier to clean it up. Not sure overall, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am also not sure if i want to rely on a later pass

needsStrictifier b =
if opts^.coPreserveStrictness
then needsNonStrictify b -- all that were non-strictified
else needsNonStrictify b && isStrictEffectful b -- introduce strictifier only for those that are impure
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmmm, I think we probably want to do it unconditionally. Strictifying things is generally good, it means we only evaluate them once. It's not an advantage to avoid it!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And as suggested above, I think you could in fact insert these for all the bindings, and the simplifier would clean it up.

-- alternative if we don't care about the original strictness, we can skip the re-strictification of those pure, non-strictified
needsStrictifier :: Binding tyname name uni fun a -> Bool
needsStrictifier b =
if opts^.coPreserveStrictness
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally I don't think this is something the user is going to care about. They might care about us messing up their effects, but I think we're generally pretty free to mess with strictness in a way that helps us.

, "monoMap"
, "errorBinding"
]
testNested "thunkRecursions"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be tempted to add some evaluation tests - that's the gold standard way to show that the effects definitely happen in a particular order!

Copy link
Contributor Author

@bezirg bezirg Sep 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also thought of adding that, but I was hit by an unrelated trace-effect missing bug

@@ -241,7 +249,7 @@ compileReadableToPlc (Program a v t) =
>=> NonStrict.compileNonStrictBindings False
>=> through check
>=> (<$ logVerbose " !!! thunkRecursions")
>=> (withBuiltinsInfo . fmap pure . flip ThunkRec.thunkRecursions)
>=> (\t' -> withBuiltinsInfo $ \semvar -> withCcOpts $ \opts -> pure $ ThunkRec.thunkRecursions semvar opts t')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at this point it's easier to just write an inline \t -> do ..., I think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this whole thing sucks a bit. I pushed the monadreader compilationctx down to each pass and then I was super excited how the code looked great. But then half of the test cases broke, so i reverted it

needsNonStrictify b =
if opts^.coPreserveEffectOrder
then isProblematic b || isStrictEffectful b
else isProblematic b
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see some discussion about why it's worth having two modes! Why not just do the conservative thing always? It doesn't look like a lot of savings: the advantage of non-conservative mode is that we keep the recursive binding strict, and so we avoid some overhead from that. But it's not clear to me how much difference that makes. And it only applies in a niche case where we have something that is of function type (so doesn't get strictified anyway), but is impure.

So I'd be pretty inclined to just make the conservative mode the only mode.

@bezirg bezirg force-pushed the bezirg/fix-thunkrecursions branch 2 times, most recently from 6dc9bae to 4d5f6c9 Compare September 26, 2023 14:28
Copy link
Contributor

@michaelpj michaelpj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@bezirg
Copy link
Contributor Author

bezirg commented Sep 27, 2023

lgtm

wait a moment I have to update 9.6 scripts output

@bezirg bezirg enabled auto-merge (squash) September 27, 2023 15:13
@bezirg bezirg merged commit 7322993 into master Sep 27, 2023
8 checks passed
@bezirg bezirg deleted the bezirg/fix-thunkrecursions branch September 27, 2023 20:12
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.

None yet

2 participants