tail call recursion with continuation passing

This commit is contained in:
andri lim 2019-04-04 11:44:35 +07:00
parent 4c0ba876ef
commit e5cca19e7f
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
3 changed files with 44 additions and 24 deletions

View File

@ -34,6 +34,9 @@ proc newBaseComputation*(vmState: BaseVMState, blockNumber: UInt256, message: Me
else:
blockNumber.toFork.forkToSchedule
result.forkOverride = forkOverride
# a dummy/terminus continuation proc
result.nextProc = proc() =
discard
proc isOriginComputation*(c: BaseComputation): bool =
# Is this computation the computation initiated by a transaction
@ -168,6 +171,8 @@ proc transferBalance(computation: BaseComputation, opCode: static[Op]) =
db.addBalance(computation.msg.storageAddress, computation.msg.value)
template continuation*(comp: BaseComputation, body: untyped) =
# this is a helper template to implement continuation
# passing and convert all recursion into tail call
var tmpNext = comp.nextProc
comp.nextProc = proc() =
body
@ -189,21 +194,27 @@ proc executeOpcodes*(computation: BaseComputation) {.gcsafe.}
proc applyMessage*(computation: BaseComputation, opCode: static[Op]) =
computation.snapshot()
defer: computation.dispose()
defer:
computation.dispose()
when opCode in {CallCode, Call, Create}:
computation.transferBalance(opCode)
if computation.isError():
computation.rollback()
computation.nextProc()
return
if computation.gasMeter.gasRemaining < 0:
computation.commit()
computation.nextProc()
return
executeOpcodes(computation)
continuation(computation):
postExecuteVM(computation)
executeOpcodes(computation)
computation.nextProc()
proc addChildComputation*(computation: BaseComputation, child: BaseComputation) =
if child.isError:
if child.msg.isCreate:

View File

@ -593,7 +593,7 @@ op create, inline = false, value, startPosition, size:
var childComp = setupCreate(computation, memPos, len, value)
if childComp.isNil: return
childComp.applyMessage(Create)
continuation(childComp):
computation.addChildComputation(childComp)
if childComp.isError:
@ -601,6 +601,8 @@ op create, inline = false, value, startPosition, size:
else:
push: childComp.msg.storageAddress
childComp.applyMessage(Create)
proc callParams(computation: BaseComputation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, MsgFlags) =
let gas = computation.stack.popInt()
let codeAddress = computation.stack.popAddress()
@ -683,7 +685,7 @@ proc staticCallParams(computation: BaseComputation): (UInt256, UInt256, EthAddre
emvcStatic) # is_static
template genCall(callName: untyped, opCode: Op): untyped =
proc `callName Setup`(computation: BaseComputation, callNameStr: string): (BaseComputation, int, int) =
proc `callName Setup`(computation: BaseComputation, callNameStr: string): BaseComputation =
let (gas, value, to, sender,
codeAddress,
memoryInputStartPosition, memoryInputSize,
@ -750,16 +752,18 @@ template genCall(callName: untyped, opCode: Op): untyped =
childMsg,
some(computation.getFork))
result = (childComp, memOutPos, memOutLen)
computation.memOutPos = memOutPos
computation.memOutLen = memOutLen
result = childComp
op callName, inline = false:
## CALL, 0xf1, Message-Call into an account
## CALLCODE, 0xf2, Message-call into this account with an alternative account's code.
## DELEGATECALL, 0xf4, Message-call into this account with an alternative account's code, but persisting the current values for sender and value.
## STATICCALL, 0xfa, Static message-call into an account.
var (childComp, memOutPos, memOutLen) = `callName Setup`(computation, callName.astToStr)
var childComp = `callName Setup`(computation, callName.astToStr)
childComp.applyMessage(opCode)
continuation(childComp):
addChildComputation(computation, childComp)
if childComp.isError:
@ -768,11 +772,13 @@ template genCall(callName: untyped, opCode: Op): untyped =
push: 1
if not childComp.shouldEraseReturnData:
let actualOutputSize = min(memOutLen, childComp.output.len)
let actualOutputSize = min(computation.memOutLen, childComp.output.len)
computation.memory.write(
memOutPos,
computation.memOutPos,
childComp.output.toOpenArray(0, actualOutputSize - 1))
childComp.applyMessage(opCode)
genCall(call, Call)
genCall(callCode, CallCode)
genCall(delegateCall, DelegateCall)

View File

@ -67,7 +67,10 @@ type
dbsnapshot*: Snapshot
instr*: Op
opIndex*: int
# continuation helpers
nextProc*: proc()
memOutLen*: int
memOutPos*: int
Error* = ref object
info*: string