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

[WIP] move things into different passes #85

Merged
merged 33 commits into from
May 10, 2021

Conversation

alaviss
Copy link
Contributor

@alaviss alaviss commented May 6, 2021

This thing is made of hacks, don't use, just look.

scratch.nim is the simple test that compile.

taste won't compile.

alaviss added 18 commits May 3, 2021 20:13
saften is for the transformation only
A lot of things doesn't work, one of them is compiling taste.

scratch.nim contains a simple test case that does compile.

This will be rewritten soon-ish.
Not sure why the compiler produces this stuff, maybe its an artifact of
desugaring `return expr` into `result = expr; return`. Except its
`return result = expr` in this case.

However we cannot rewrite this into `result = expr; return`, because
the compiler wouldn't like that if the proc is nested within an another
proc that returns (due to closure capture analysis), even if you use the
`result` symbol within the return statement (the compiler is broken beyond
help).
Sem-ed pragmas that are not built in will be rewritten into calls
or a colon expression.

This commit add support for detecting and filtering those pragmas.
Instead of making cpsJump an annotation so that other scope
macros may direct the flow, we employ the use of a custom pragma
as an annotation to where the control-flow continuation should
be inserted.

This drastically simplifies the work that scope macros will have to
do, since all transformations would have been done before they reaches
the scope macros.
as discussed with disruptek
This allows the code to be shared between cps transforms macros.
This is a simple primitive that says: Going from `a` to `b` might not be a
linear transition because `a` might be tainted by a cpsJump.

It doesn't work on the simple sample yet, got some internal errors.
This keeps the sem-ed result of each replacement separated, preventing weird
compiler errors.
Got rid of some crashes but now we got varargs issues.
normalizingRewrites is now the universal tool to turn typed AST into
something that's safe to consume.

This also makes normalizingRewrites recurse into what it rewrote.
These macros modify call nodes, so they have to be rewritten to force
sem to re-evaluate the parameters.
We also normalize the call so that it's safe to remove, a bug
discovered on more complex tests.
sem might flatten the AST so that a tree like this:

StmtList
  IfStmt

got passed to those macros as:

IfStmt

We workaround this issue by putting things in nnkStmtList as needed.
@disruptek
Copy link
Contributor

Looks great and I hope we don't have to hold this up with getting anything more than tzevv blocks to pass. There's already a newly-working taste test and this is already good work we shouldn't have to review twice. 😁

cps.nim Outdated Show resolved Hide resolved
cps.nim Outdated Show resolved Hide resolved
cps.nim Outdated
Comment on lines 514 to 523
when false:
# if the child is a cps block (not a call), then push a tailcall
# onto the stack during the saftening of the child
if nc.kind notin unexiter and nc.isCpsBlock and not nc.isCpsCall:
withGoto env.splitAt(n, i, "done"):
result.doc "saftening during done"
result.add env.saften(nc)
# we've completed the split, so we're done here
return
else:
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's just get rid of it.

cps.nim Outdated
Comment on lines 496 to 511
else:
# we want to make sure that a pop inside the after body doesn't
# return to after itself, so we don't add it to the goto list...

let after = splitAt(env, n, i, "afterCall")
result.add:
tailCall(env, env.saften nc):
returnTo after
# include the definition for the after proc
if after.node.kind != nnkSym:
# add the proc definition and declaration without the return
for child in after.node.items:
if child.kind == nnkProcDef:
result.add child
# done!
return
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's just get rid of it.

cps.nim Outdated
Comment on lines 770 to 785
proc replacePending(n, replacement: NimNode): NimNode =
## Replace cpsPending annotations with something else, usually
## a jump to an another location. If `replacement` is nil, remove
## the annotation.
proc resolved(n: NimNode): NimNode =
case n.kind
of nnkPragma:
# a {.cpsPending.} annotation is a standalone pragma statement
if n.len == 1 and n.hasPragma("cpsPending"):
if replacement.isNil:
result = newEmptyNode()
else:
result = copyNimTree replacement
else: discard

result = filter(n, resolved)
Copy link
Contributor

Choose a reason for hiding this comment

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

Prefer an additional version that takes no replacement argument and does the right thing, versus a version that has special logic to handle a sentinel nil. The current version should demand that the replacement is not nil.

cps.nim Outdated
# make `n` safe for modification
let n = normalizingRewrites n
# replace all `pending` with `return nil`, signifying end of continuations
result = replacePending(n, tailCall(nil, nil))
Copy link
Contributor

Choose a reason for hiding this comment

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

Prefer not providing replacePending a second argument, but failing that...
Prefer a tailCall that does the right thing with no input... same as my prior feedback; this is very smelly. 😁


debug(".cpsStripPending.", result, akOriginal, n)

proc xfrmFloat(n: NimNode): NimNode =
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe another rewrite name like rewriteLift is more obvious?

Copy link
Contributor

Choose a reason for hiding this comment

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

Lift brings it into the CPS context, more precise than rewrite being flatten?

tests/scratch.nim Outdated Show resolved Hide resolved
cps/spec.nim Outdated
Comment on lines 128 to 139
let
info =
if info.isNil:
n
else:
info
lineInfo = info.lineInfoObj
procName =
if info.kind in RoutineNodes:
repr info.name
else:
""
Copy link
Contributor

Choose a reason for hiding this comment

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

Feel free to make this 3-4 lines.

alaviss and others added 10 commits May 8, 2021 13:59
There is no harm in wrapping multiple StmtList

Co-authored-by: Smooth Operator <disruptek@users.noreply.github.com>
The new branch is starting to work well now.
This is no longer necessary now that taste compiles
Named block, simple block, all supported.
differences with previous:
- all block statements are rewritten if they contain a cpsJump, regardless of their
  position in the AST. This is required due to interactions with {.cpsBreak.}
- all {.cpsBreak.}, {.cpsContinue.} are now cleaned up on-site if
  the while/block is not a cps block.
We happened to fix a bug in the process.
The "end of continuation" has been moved into a dedicated proc.
AstKind is now a {.pure.} enum, and we fixes some erroneous annotations
This is a huge simplification of rewrite that we can look to exploit
in the future.

Annotations creation are also moved to spec.
else:
# {.cpsBreak.}
tail
func matchCpsBreak(label: NimNode): Matcher =
Copy link
Contributor

Choose a reason for hiding this comment

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

matcherCpsBreak, since it creates one rather than matches it? Not a blocker, cpsBreakMatcher reads better but the lack of symmetry sucks for grouping related things. I can do this later too.

@disruptek disruptek marked this pull request as ready for review May 10, 2021 16:00
@disruptek disruptek merged commit 9c40f1f into nim-works:master May 10, 2021
@disruptek
Copy link
Contributor

Really excellent work, @alaviss! 😘

@alaviss alaviss deleted the wip-passes branch May 11, 2021 02:58
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.

Splitting inside an if-else generates broken ast Block rewriting error when there are no explicit break
3 participants