mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-12 13:24:21 +00:00
simplify interpreter_dispatch.nim code
details: replace generated macro loop/switch by explicit call using the fork/op handler matrix (encapsulated via opHandlersRun() function)
This commit is contained in:
parent
2fb18bf88c
commit
b388e966cc
@ -63,7 +63,7 @@ proc mkOpTable(select: Fork): array[Op,Vm2OpExec] {.compileTime.} =
|
|||||||
result[op].name = "toBeReplacedByBreak"
|
result[op].name = "toBeReplacedByBreak"
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public handler tables
|
# Public functions
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
const
|
const
|
||||||
@ -73,39 +73,66 @@ const
|
|||||||
rc[w] = w.mkOpTable
|
rc[w] = w.mkOpTable
|
||||||
rc
|
rc
|
||||||
|
|
||||||
|
type
|
||||||
|
hdlRec = tuple
|
||||||
|
name: string
|
||||||
|
info: string
|
||||||
|
run: Vm2OpFn
|
||||||
|
|
||||||
|
const
|
||||||
|
# Pack handler record.
|
||||||
|
#
|
||||||
|
# Important:
|
||||||
|
# As of NIM 1.2.10, this mapping to another record is crucial for
|
||||||
|
#
|
||||||
|
# vmOpHandlers[fork][op].run
|
||||||
|
#
|
||||||
|
# to pick right function when <op> is a variable . Using
|
||||||
|
#
|
||||||
|
# vm2OpHandlers[fork][op].exec.run
|
||||||
|
#
|
||||||
|
# only works when <op> is a constant. There seems to be some optimisation
|
||||||
|
# that garbles the <exec> sub-structures elements <prep>, <run>, and <post>.
|
||||||
|
# Moreover, <post> is seen NULL under the debugger. It is untested yet
|
||||||
|
# under what circumstances the vm2OpHandlers[] matrix is set up correctly.
|
||||||
|
# Linearising/flattening the index has no effect here.
|
||||||
|
#
|
||||||
|
vmOpHandlers = block:
|
||||||
|
var rc: array[Fork, array[Op, hdlRec]]
|
||||||
|
for fork in Fork:
|
||||||
|
var tab = fork.mkOpTable
|
||||||
|
for op in Op:
|
||||||
|
rc[fork][op].name = tab[op].name
|
||||||
|
rc[fork][op].info = tab[op].info
|
||||||
|
rc[fork][op].run = tab[op].exec.run
|
||||||
|
rc
|
||||||
|
|
||||||
|
proc opHandlersRun*(fork: Fork; op: Op; d: Vm2Ctx) {.inline.} =
|
||||||
|
## Given a particular `fork` and an `op`-code, run the associated handler
|
||||||
|
vmOpHandlers[fork][op].run(d)
|
||||||
|
|
||||||
|
proc opHandlersName*(fork: Fork; op: Op): string =
|
||||||
|
## Get name (or ID) of op handler
|
||||||
|
vmOpHandlers[fork][op].name
|
||||||
|
|
||||||
|
proc opHandlersInfo*(fork: Fork; op: Op): string =
|
||||||
|
## Get some op handler info
|
||||||
|
vmOpHandlers[fork][op].info
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Debugging ...
|
# Debugging ...
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
when isMainModule and isNoisy:
|
when isMainModule and isNoisy:
|
||||||
var dummy = 0
|
echo ">>> berlin[shl]: ", FkBerlin.opHandlersInfo(Shl)
|
||||||
proc gdbBPSink() =
|
echo ">>> berlin[push32]: ", FkBerlin.opHandlersInfo(Push32)
|
||||||
dummy.inc
|
echo ">>> berlin[dup16]: ", FkBerlin.opHandlersInfo(Dup16)
|
||||||
|
echo ">>> berlin[swap16]: ", FkBerlin.opHandlersInfo(Swap16)
|
||||||
|
echo ">>> berlin[log4]: ", FkBerlin.opHandlersInfo(Log4)
|
||||||
|
|
||||||
gdbBPSink()
|
echo ">>> frontier[sstore]: ", FkFrontier.opHandlersInfo(Sstore)
|
||||||
echo ">>> berlin[shl]: ",
|
echo ">>> constantinople[sstore]: ", FkConstantinople.opHandlersInfo(Sstore)
|
||||||
vm2OpHandlers[FkBerlin][Shl].info
|
echo ">>> berlin[sstore]: ", FkBerlin.opHandlersInfo(Sstore)
|
||||||
|
|
||||||
echo ">>> berlin[push32]: ",
|
|
||||||
vm2OpHandlers[FkBerlin][Push32].info
|
|
||||||
|
|
||||||
echo ">>> berlin[dup16]: ",
|
|
||||||
vm2OpHandlers[FkBerlin][Dup16].info
|
|
||||||
|
|
||||||
echo ">>> berlin[swap16]: ",
|
|
||||||
vm2OpHandlers[FkBerlin][Swap16].info
|
|
||||||
|
|
||||||
echo ">>> berlin[log4]: ",
|
|
||||||
vm2OpHandlers[FkBerlin][Log4].info
|
|
||||||
|
|
||||||
echo ">>> frontier[sstore]: ",
|
|
||||||
vm2OpHandlers[FkFrontier][Sstore].info
|
|
||||||
|
|
||||||
echo ">>> constantinople[sstore]: ",
|
|
||||||
vm2OpHandlers[FkConstantinople][Sstore].info
|
|
||||||
|
|
||||||
echo ">>> berlin[sstore]: ",
|
|
||||||
vm2OpHandlers[FkBerlin][Sstore].info
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# End
|
# End
|
||||||
|
@ -114,8 +114,7 @@ type
|
|||||||
|
|
||||||
const
|
const
|
||||||
vm2OpIgnore*: Vm2OpFn = ## No operation, placeholder function
|
vm2OpIgnore*: Vm2OpFn = ## No operation, placeholder function
|
||||||
proc(k: Vm2Ctx) {.gcsafe.} =
|
proc(k: Vm2Ctx) = discard
|
||||||
discard
|
|
||||||
|
|
||||||
# similar to: toSeq(Fork).mapIt({it}).foldl(a+b)
|
# similar to: toSeq(Fork).mapIt({it}).foldl(a+b)
|
||||||
Vm2OpAllForks* =
|
Vm2OpAllForks* =
|
||||||
|
@ -6,156 +6,53 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
tables, macros,
|
|
||||||
chronicles,
|
chronicles,
|
||||||
./interpreter/op_handlers/oph_defs,
|
./interpreter/[gas_meter, op_handlers, op_handlers/oph_defs, v2gas_costs],
|
||||||
./interpreter/[forks_list, op_handlers, op_codes, v2gas_costs, gas_meter],
|
./code_stream, ./v2types, ./v2precompiles
|
||||||
./code_stream, ./v2types, ./v2precompiles, ./stack
|
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "vm opcode"
|
topics = "vm opcode"
|
||||||
|
|
||||||
proc opTableToCaseStmt(c: NimNode; fork: Fork): NimNode =
|
|
||||||
|
|
||||||
let instr = quote do: `c`.instr
|
|
||||||
result = nnkCaseStmt.newTree(instr)
|
|
||||||
|
|
||||||
# Add a branch for each (opcode, proc) pair
|
|
||||||
# We dispatch to the next instruction at the end of each branch
|
|
||||||
for op in Op:
|
|
||||||
let asOp = quote do: Op(`op`) # TODO: unfortunately when passing to runtime, ops are transformed into int
|
|
||||||
let branchStmt = block:
|
|
||||||
if op == Stop:
|
|
||||||
quote do:
|
|
||||||
trace "op: Stop"
|
|
||||||
if not `c`.code.atEnd() and `c`.tracingEnabled:
|
|
||||||
# we only trace `REAL STOP` and ignore `FAKE STOP`
|
|
||||||
`c`.opIndex = `c`.traceOpCodeStarted(`asOp`)
|
|
||||||
`c`.traceOpCodeEnded(`asOp`, `c`.opIndex)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if BaseGasCosts[op].kind == GckFixed:
|
|
||||||
quote do:
|
|
||||||
if `c`.tracingEnabled:
|
|
||||||
`c`.opIndex = `c`.traceOpCodeStarted(`asOp`)
|
|
||||||
`c`.gasMeter.consumeGas(`c`.gasCosts[`asOp`].cost, reason = $`asOp`)
|
|
||||||
var desc: Vm2Ctx
|
|
||||||
desc.cpt = `c`
|
|
||||||
vm2OpHandlers[Fork(`fork`)][`asOp`].exec.run(desc)
|
|
||||||
if `c`.tracingEnabled:
|
|
||||||
`c`.traceOpCodeEnded(`asOp`, `c`.opIndex)
|
|
||||||
when `asOp` in {Create, Create2, Call, CallCode, DelegateCall, StaticCall}:
|
|
||||||
if not `c`.continuation.isNil:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
quote do:
|
|
||||||
if `c`.tracingEnabled:
|
|
||||||
`c`.opIndex = `c`.traceOpCodeStarted(`asOp`)
|
|
||||||
var desc: Vm2Ctx
|
|
||||||
desc.cpt = `c`
|
|
||||||
vm2OpHandlers[Fork(`fork`)][`asOp`].exec.run(desc)
|
|
||||||
if `c`.tracingEnabled:
|
|
||||||
`c`.traceOpCodeEnded(`asOp`, `c`.opIndex)
|
|
||||||
when `asOp` in {Create, Create2, Call, CallCode, DelegateCall, StaticCall}:
|
|
||||||
if not `c`.continuation.isNil:
|
|
||||||
return
|
|
||||||
when `asOp` in {Return, Revert, SelfDestruct}:
|
|
||||||
break
|
|
||||||
|
|
||||||
result.add nnkOfBranch.newTree(
|
|
||||||
newIdentNode($op),
|
|
||||||
branchStmt
|
|
||||||
)
|
|
||||||
|
|
||||||
# Wrap the case statement in while true + computed goto
|
|
||||||
result = quote do:
|
|
||||||
if `c`.tracingEnabled:
|
|
||||||
`c`.prepareTracer()
|
|
||||||
while true:
|
|
||||||
`instr` = `c`.code.next()
|
|
||||||
#{.computedGoto.}
|
|
||||||
# computed goto causing stack overflow, it consumes a lot of space
|
|
||||||
# we could use manual jump table instead
|
|
||||||
# TODO lots of macro magic here to unravel, with chronicles...
|
|
||||||
# `c`.logger.log($`c`.stack & "\n\n", fgGreen)
|
|
||||||
`result`
|
|
||||||
|
|
||||||
|
|
||||||
macro genFrontierDispatch(c: Computation): untyped =
|
|
||||||
result = opTableToCaseStmt(c, FkFrontier)
|
|
||||||
|
|
||||||
macro genHomesteadDispatch(c: Computation): untyped =
|
|
||||||
result = opTableToCaseStmt(c, FkHomestead)
|
|
||||||
|
|
||||||
macro genTangerineDispatch(c: Computation): untyped =
|
|
||||||
result = opTableToCaseStmt(c, FkTangerine)
|
|
||||||
|
|
||||||
macro genSpuriousDispatch(c: Computation): untyped =
|
|
||||||
result = opTableToCaseStmt(c, FkSpurious)
|
|
||||||
|
|
||||||
macro genByzantiumDispatch(c: Computation): untyped =
|
|
||||||
result = opTableToCaseStmt(c, FkByzantium)
|
|
||||||
|
|
||||||
macro genConstantinopleDispatch(c: Computation): untyped =
|
|
||||||
result = opTableToCaseStmt(c, FkConstantinople)
|
|
||||||
|
|
||||||
macro genPetersburgDispatch(c: Computation): untyped =
|
|
||||||
result = opTableToCaseStmt(c, FkPetersburg)
|
|
||||||
|
|
||||||
macro genIstanbulDispatch(c: Computation): untyped =
|
|
||||||
result = opTableToCaseStmt(c, FkIstanbul)
|
|
||||||
|
|
||||||
macro genBerlinDispatch(c: Computation): untyped =
|
|
||||||
result = opTableToCaseStmt(c, FkBerlin)
|
|
||||||
|
|
||||||
proc frontierVM(c: Computation) =
|
|
||||||
genFrontierDispatch(c)
|
|
||||||
|
|
||||||
proc homesteadVM(c: Computation) =
|
|
||||||
genHomesteadDispatch(c)
|
|
||||||
|
|
||||||
proc tangerineVM(c: Computation) =
|
|
||||||
genTangerineDispatch(c)
|
|
||||||
|
|
||||||
proc spuriousVM(c: Computation) {.gcsafe.} =
|
|
||||||
genSpuriousDispatch(c)
|
|
||||||
|
|
||||||
proc byzantiumVM(c: Computation) {.gcsafe.} =
|
|
||||||
genByzantiumDispatch(c)
|
|
||||||
|
|
||||||
proc constantinopleVM(c: Computation) {.gcsafe.} =
|
|
||||||
genConstantinopleDispatch(c)
|
|
||||||
|
|
||||||
proc petersburgVM(c: Computation) {.gcsafe.} =
|
|
||||||
genPetersburgDispatch(c)
|
|
||||||
|
|
||||||
proc istanbulVM(c: Computation) {.gcsafe.} =
|
|
||||||
genIstanbulDispatch(c)
|
|
||||||
|
|
||||||
proc berlinVM(c: Computation) {.gcsafe.} =
|
|
||||||
genBerlinDispatch(c)
|
|
||||||
|
|
||||||
proc selectVM(c: Computation, fork: Fork) {.gcsafe.} =
|
proc selectVM(c: Computation, fork: Fork) {.gcsafe.} =
|
||||||
# TODO: Optimise getting fork and updating opCodeExec only when necessary
|
if c.tracingEnabled:
|
||||||
case fork
|
c.prepareTracer()
|
||||||
of FkFrontier:
|
|
||||||
c.frontierVM()
|
while true:
|
||||||
of FkHomestead:
|
c.instr = c.code.next()
|
||||||
c.homesteadVM()
|
|
||||||
of FkTangerine:
|
var
|
||||||
c.tangerineVM()
|
op = c.instr
|
||||||
of FkSpurious:
|
desc: Vm2Ctx
|
||||||
c.spuriousVM()
|
|
||||||
of FkByzantium:
|
desc.cpt = c
|
||||||
c.byzantiumVM()
|
|
||||||
of FkConstantinople:
|
if op == Stop:
|
||||||
c.constantinopleVM()
|
trace "op: Stop"
|
||||||
of FkPetersburg:
|
if not c.code.atEnd() and c.tracingEnabled:
|
||||||
c.petersburgVM()
|
c.opIndex = c.traceOpCodeStarted(Stop)
|
||||||
of FkIstanbul:
|
c.traceOpCodeEnded(Stop, c.opIndex)
|
||||||
c.istanbulVM()
|
break
|
||||||
else:
|
|
||||||
c.berlinVM()
|
if c.tracingEnabled:
|
||||||
|
c.opIndex = c.traceOpCodeStarted(op)
|
||||||
|
if BaseGasCosts[op].kind == GckFixed:
|
||||||
|
c.gasMeter.consumeGas(c.gasCosts[op].cost, reason = $op)
|
||||||
|
|
||||||
|
opHandlersRun(fork, op, desc)
|
||||||
|
|
||||||
|
if c.tracingEnabled:
|
||||||
|
c.traceOpCodeEnded(op, c.opIndex)
|
||||||
|
|
||||||
|
case op
|
||||||
|
of Create, Create2, Call, CallCode, DelegateCall, StaticCall:
|
||||||
|
if not c.continuation.isNil:
|
||||||
|
return
|
||||||
|
of Return, Revert, SelfDestruct:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
|
|
||||||
proc executeOpcodes(c: Computation) =
|
proc executeOpcodes(c: Computation) =
|
||||||
let fork = c.fork
|
let fork = c.fork
|
||||||
|
Loading…
x
Reference in New Issue
Block a user