simplifies code, remove globals and double layer macro, employ macrocache

This commit is contained in:
andri lim 2019-01-31 13:35:07 +07:00 committed by zah
parent fff049abe8
commit 99a5eefcce
1 changed files with 74 additions and 200 deletions

View File

@ -1,6 +1,7 @@
import
macros, strutils, unittest, byteutils, chronicles,
../nimbus/vm/interpreter/opcode_values, ranges, eth_common
macros, macrocache, strutils, unittest,
byteutils, chronicles, ranges, eth_common,
../nimbus/vm/interpreter/opcode_values
import
options, json, os, eth_trie/[db, hexary],
@ -31,111 +32,16 @@ type
data*: seq[byte]
output*: seq[byte]
OpcodeDesc = object
numParams: int
var
g_asm {.compileTime.}: Assembler
g_code {.compileTime.}: NimNode
g_lookup {.compileTime.}: array[256, OpcodeDesc]
proc writeLUT(opcode: Op, numParams: int) {.compileTime.} =
g_lookup[ord(opcode)] = OpcodeDesc(numParams: numParams)
proc initializeLUT() {.compileTime.} =
writeLUT(Stop, 0)
writeLUT(Add, 2)
writeLUT(Mul, 2)
writeLUT(Sub, 2)
writeLUT(Div, 2)
writeLUT(Sdiv, 2)
writeLUT(Mod, 2)
writeLUT(Smod, 2)
writeLUT(Addmod, 3)
writeLUT(Mulmod, 3)
writeLUT(Exp, 2)
writeLUT(SignExtend, 2)
writeLUT(Lt, 2)
writeLUT(Gt, 2)
writeLUT(Slt, 2)
writeLUT(Sgt, 2)
writeLUT(Eq, 2)
writeLUT(IsZero, 1)
writeLUT(And, 2)
writeLUT(Or, 2)
writeLUT(Xor, 2)
writeLUT(Not, 1)
writeLUT(Byte, 2)
writeLUT(Sha3, 2)
writeLUT(Address, 0)
writeLUT(Balance, 1)
writeLUT(Origin, 0)
writeLUT(Caller, 0)
writeLUT(CallValue, 0)
writeLUT(CallDataLoad, 1)
writeLUT(CallDataSize, 0)
writeLUT(CallDataCopy, 3)
writeLUT(CodeSize, 0)
writeLUT(CodeCopy, 3)
writeLUT(GasPrice, 0)
writeLUT(ExtCodeSize, 1)
writeLUT(ExtCodeCopy, 4)
writeLUT(ReturnDataSize, 0)
writeLUT(ReturnDataCopy, 3)
writeLUT(Blockhash, 1)
writeLUT(Coinbase, 0)
writeLUT(Timestamp, 0)
writeLUT(Number, 0)
writeLUT(Difficulty, 0)
writeLUT(GasLimit, 0)
writeLUT(Pop, 1)
writeLUT(Mload, 1)
writeLUT(Mstore, 2)
writeLUT(Mstore8, 2)
writeLUT(Sload, 1)
writeLUT(Sstore, 2)
writeLUT(Jump, 1)
writeLUT(JumpI, 2)
writeLUT(Pc, 0)
writeLUT(Msize, 0)
writeLUT(Gas, 0)
writeLUT(JumpDest, 0)
for i in Push1 .. Push32:
writeLUT(i, 1)
for i in Dup1 .. Dup16:
writeLUT(i, 0)
for i in Swap1 .. Swap16:
writeLUT(i, 0)
for i in Log0 .. Log4:
writeLUT(i, 0)
writeLUT(Create, 3)
writeLUT(Call, 7)
writeLUT(CallCode, 7)
writeLUT(Return, 2)
writeLUT(DelegateCall, 6)
writeLUT(StaticCall, 6)
writeLUT(Revert, 2)
writeLUT(Invalid, 1)
writeLUT(SelfDestruct, 1)
const
idToOpcode = CacheTable"NimbusMacroAssembler"
static:
initializeLUT()
for n in Op:
idToOpcode[$n] = newLit(ord(n))
proc validateVMWord(val: string, n: NimNode): VMWord =
if val.len <= 2 or val.len > 66:
error("invalid hex string", n)
if not (val[0] == '0' and val[1] == 'x'):
error("invalid hex string", n)
if val.len <= 2 or val.len > 66: error("invalid hex string", n)
if not (val[0] == '0' and val[1] == 'x'): error("invalid hex string", n)
let zerosLen = 64 - (val.len - 2)
let value = repeat('0', zerosLen) & val.substr(2)
hexToByteArray(value, result)
@ -176,44 +82,88 @@ proc parseData(list: NimNode): seq[byte] =
n.expectKind(nnkStrLit)
result.add hexToSeqByte(n.strVal)
proc parseLog(node: NimNode): Log =
node.expectKind(nnkPar)
for item in node:
item.expectKind(nnkExprColonExpr)
let label = item[0].strVal
let body = item[1]
case label.normalize
of "address":
body.expectKind(nnkStrLit)
let value = body.strVal
if value.len < 20:
error("bad address format", body)
hexToByteArray(value, result.address)
of "topics":
body.expectKind(nnkBracket)
for x in body:
result.topics.add validateVMWord(x.strVal, x)
of "data":
result.data = hexToSeqByte(body.strVal)
else:error("unknown log section '" & label & "'", item[0])
proc parseLogs(list: NimNode): seq[Log] =
result = @[]
list.expectKind nnkStmtList
for n in list:
result.add parseLog(n)
proc validateOpcode(sym: NimNode) =
let typ = getTypeInst(sym)
typ.expectKind(nnkSym)
if $typ != "Op":
error("unknown opcode '" & $sym & "'", sym)
proc addOpCode(opcode: Op, node: NimNode, params: varargs[string]) =
let lut = g_lookup[opcode.ord]
if lut.numParams > 0 and params.len > 0:
if lut.numParams != params.len:
error("Opcode '" & $opcode & "' expect " & $lut.numParams & " params, but got " & $params.len, node)
proc addOpCode(code: var seq[byte], node, params: NimNode) =
node.expectKind nnkSym
let opcode = Op(idToOpcode[node.strVal].intVal)
case opcode
of Push1..Push32:
if params.len != 1:
error("expect 1 param, but got " & $params.len, node)
let paramWidth = (opcode.ord - 95) * 2
var val = params[0]
params[0].expectKind nnkStrLit
var val = params[0].strVal
if val[0] == '0' and val[1] == 'x':
val = val.substr(2)
if val.len != paramWidth:
error("expected param with " & $paramWidth & " hex digits, got " & $val.len, node)
g_asm.code.add byte(opcode)
g_asm.code.add hexToSeqByte(val)
code.add byte(opcode)
code.add hexToSeqByte(val)
else:
error("invalid hex format", node)
else:
if params.len > 0:
for i in countDown(params.len - 1, 0):
g_asm.code.add byte(Push32)
g_asm.code.add validateVMWord(params[i], node)
g_asm.code.add byte(opcode)
error("there should be no param for this instruction", node)
code.add byte(opcode)
proc addLiteral(node: NimNode, rawCode: string) =
if rawCode[0] == '0' and rawCode[1] == 'x':
let val = rawCode.substr(2)
g_asm.code.add hexToSeqByte(val)
else:
error("invalid hex format", node)
proc parseCode(codes: NimNode): seq[byte] =
let emptyNode = newEmptyNode()
codes.expectKind nnkStmtList
var addStop = true
for pc, line in codes:
line.expectKind({nnkCommand, nnkIdent, nnkStrLit})
if line.kind == nnkStrLit:
result.add hexToSeqByte(line.strVal)
elif line.kind == nnkIdent:
let sym = bindSym(line)
validateOpcode(sym)
result.addOpCode(sym, emptyNode)
if pc == codes.len - 1:
addStop = $sym != "Stop"
elif line.kind == nnkCommand:
let sym = bindSym(line[0])
validateOpcode(sym)
var params = newNimNode(nnkBracket)
for i in 1 ..< line.len:
params.add line[i]
result.addOpCode(sym, params)
else:
error("unknown syntax: " & line.toStrLit.strVal, line)
if addStop:
result.addOpCode(bindSym"Stop", emptyNode)
proc generateVMProxy(boa: Assembler): NimNode =
let vmProxy = genSym(nskProc, "vmProxy")
@ -309,54 +259,6 @@ proc generateVMProxy(boa: Assembler): NimNode =
when defined(macro_assembler_debug):
echo result.toStrLit.strVal
proc assemblerImpl(boa: var Assembler, codes: NimNode): NimNode =
g_code = codes
boa.code = @[]
codes.expectKind nnkStmtList
let macroName = genSym(nskMacro, "asmProxy")
var addStop = true
var body = newStmtList()
for pc, line in codes:
line.expectKind({nnkCommand, nnkIdent, nnkStrLit})
if line.kind == nnkStrLit:
body.add quote do:
addLiteral(g_code[`pc`], `line`)
elif line.kind == nnkIdent:
let sym = bindSym(line)
validateOpcode(sym)
body.add quote do:
addOpCode(`sym`, g_code[`pc`])
if pc == codes.len - 1:
if normalize($sym) == "stop":
addStop = false
elif line.kind == nnkCommand:
let ident = line[0]
let sym = bindSym(ident)
validateOpcode(sym)
var params = newNimNode(nnkBracket)
for i in 1 ..< line.len:
params.add line[i]
body.add quote do:
addOpCode(`sym`, g_code[`pc`], `params`)
else:
error("unknown syntax: " & line.toStrLit.strVal, line)
let stop = ident("Stop")
if addStop:
body.add quote do:
addOpCode(`stop`, newEmptyNode())
let resIdent = ident("result")
result = quote do:
macro `macroName`(): untyped =
`body`
`resIdent` = generateVMProxy(g_asm)
`macroName`()
when defined(macro_assembler_debug):
echo result.toStrLit.strVal
const
blockFile = "tests" / "fixtures" / "PersistBlockTests" / "block47205.json"
@ -500,36 +402,8 @@ proc runVM*(blockNumber: Uint256, chainDB: BaseChainDB, boa: Assembler): bool =
result = true
proc parseLog(node: NimNode): Log =
node.expectKind(nnkPar)
for item in node:
item.expectKind(nnkExprColonExpr)
let label = item[0].strVal
let body = item[1]
case label.normalize
of "address":
body.expectKind(nnkStrLit)
let value = body.strVal
if value.len < 20:
error("bad address format", body)
hexToByteArray(value, result.address)
of "topics":
body.expectKind(nnkBracket)
for x in body:
result.topics.add validateVMWord(x.strVal, x)
of "data":
result.data = hexToSeqByte(body.strVal)
else:error("unknown log section '" & label & "'", item[0])
proc parseLogs(list: NimNode): seq[Log] =
result = @[]
list.expectKind nnkStmtList
for n in list:
result.add parseLog(n)
macro assembler*(list: untyped): untyped =
var boa: Assembler
boa.success = true
var boa = Assembler(success: true)
list.expectKind nnkStmtList
for callSection in list:
callSection.expectKind(nnkCall)
@ -540,7 +414,7 @@ macro assembler*(list: untyped): untyped =
let title = body[0]
title.expectKind(nnkStrLit)
boa.title = title.strVal
of "code" : result = assemblerImpl(boa, body)
of "code" : boa.code = parseCode(body)
of "memory": boa.memory = parseVMWords(body)
of "stack" : boa.stack = parseVMWords(body)
of "storage": boa.storage = parseStorage(body)
@ -549,4 +423,4 @@ macro assembler*(list: untyped): untyped =
of "data": boa.data = parseData(body)
of "output": boa.output = parseData(body)
else: error("unknown section '" & label & "'", callSection[0])
g_asm = boa
result = boa.generateVMProxy()