Skip to content

Commit

Permalink
EVM: Eliminate recursion entirely
Browse files Browse the repository at this point in the history
This patch eliminates recursion entirely from the EVM when ENABLE_EVMC=0.

Signed-off-by: Jamie Lokier <[email protected]>
  • Loading branch information
jlokier committed Apr 20, 2021
1 parent e0f5a5e commit 085661c
Showing 1 changed file with 17 additions and 25 deletions.
42 changes: 17 additions & 25 deletions nimbus/vm/computation.nim
Original file line number Diff line number Diff line change
Expand Up @@ -309,36 +309,28 @@ template chainTo*(c, toChild: Computation, after: untyped) =
c.continuation = proc() =
after

proc execCallOrCreateAux(c: var Computation) {.noinline.} =
# Perform recursion with minimum-size stack per level. The exception
# handling is very subtle. Each call to `snapshot` must have a corresponding
# `dispose` on exception. To minimise this proc's stackframe, `defer` is
# moved to the outermost proc only. `{.noinline.}` is also used to make
# extra sure they stay separate. `c` is a `var` parameter at every level of
# recursion, so the outermost proc sees every change to `c`, which is why `c`
# is updated instead of using `let`. On exception, the outermost `defer`
# walks the `c.parent` chain to call `dispose` on each `c`.
if c.beforeExec():
return
c.executeOpcodes()
while not c.continuation.isNil:
# Parent and child refs are updated and cleared so as to avoid circular
# refs (like a double-linked list) or dangling refs (to finished child).
(c.child, c, c.parent) = (nil.Computation, c.child, c)
execCallOrCreateAux(c)
c.dispose()
(c.parent, c) = (nil.Computation, c.parent)
(c.continuation)()
c.executeOpcodes()
c.afterExec()

proc execCallOrCreate*(cParam: Computation) =
var c = cParam
var (c, before) = (cParam, true)
defer:
while not c.isNil:
c.dispose()
c = c.parent
execCallOrCreateAux(c)

# No actual recursion, but simulate recursion including before/after/dispose.
while true:
while true:
if before and c.beforeExec():
break
c.executeOpcodes()
if c.continuation.isNil:
c.afterExec()
break
(before, c.child, c, c.parent) = (true, nil.Computation, c.child, c)
if c.parent.isNil:
break
c.dispose()
(before, c.parent, c) = (false, nil.Computation, c.parent)
(c.continuation)()

proc merge*(c, child: Computation) =
c.logEntries.add child.logEntries
Expand Down

0 comments on commit 085661c

Please sign in to comment.