simplifies code, remove globals and double layer macro, employ macrocache
This commit is contained in:
parent
fff049abe8
commit
99a5eefcce
|
@ -1,6 +1,7 @@
|
||||||
import
|
import
|
||||||
macros, strutils, unittest, byteutils, chronicles,
|
macros, macrocache, strutils, unittest,
|
||||||
../nimbus/vm/interpreter/opcode_values, ranges, eth_common
|
byteutils, chronicles, ranges, eth_common,
|
||||||
|
../nimbus/vm/interpreter/opcode_values
|
||||||
|
|
||||||
import
|
import
|
||||||
options, json, os, eth_trie/[db, hexary],
|
options, json, os, eth_trie/[db, hexary],
|
||||||
|
@ -31,111 +32,16 @@ type
|
||||||
data*: seq[byte]
|
data*: seq[byte]
|
||||||
output*: seq[byte]
|
output*: seq[byte]
|
||||||
|
|
||||||
OpcodeDesc = object
|
const
|
||||||
numParams: int
|
idToOpcode = CacheTable"NimbusMacroAssembler"
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
static:
|
static:
|
||||||
initializeLUT()
|
for n in Op:
|
||||||
|
idToOpcode[$n] = newLit(ord(n))
|
||||||
|
|
||||||
proc validateVMWord(val: string, n: NimNode): VMWord =
|
proc validateVMWord(val: string, n: NimNode): VMWord =
|
||||||
if val.len <= 2 or val.len > 66:
|
if val.len <= 2 or val.len > 66: error("invalid hex string", n)
|
||||||
error("invalid hex string", n)
|
if not (val[0] == '0' and val[1] == 'x'): 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 zerosLen = 64 - (val.len - 2)
|
||||||
let value = repeat('0', zerosLen) & val.substr(2)
|
let value = repeat('0', zerosLen) & val.substr(2)
|
||||||
hexToByteArray(value, result)
|
hexToByteArray(value, result)
|
||||||
|
@ -176,44 +82,88 @@ proc parseData(list: NimNode): seq[byte] =
|
||||||
n.expectKind(nnkStrLit)
|
n.expectKind(nnkStrLit)
|
||||||
result.add hexToSeqByte(n.strVal)
|
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) =
|
proc validateOpcode(sym: NimNode) =
|
||||||
let typ = getTypeInst(sym)
|
let typ = getTypeInst(sym)
|
||||||
typ.expectKind(nnkSym)
|
typ.expectKind(nnkSym)
|
||||||
if $typ != "Op":
|
if $typ != "Op":
|
||||||
error("unknown opcode '" & $sym & "'", sym)
|
error("unknown opcode '" & $sym & "'", sym)
|
||||||
|
|
||||||
proc addOpCode(opcode: Op, node: NimNode, params: varargs[string]) =
|
proc addOpCode(code: var seq[byte], node, params: NimNode) =
|
||||||
let lut = g_lookup[opcode.ord]
|
node.expectKind nnkSym
|
||||||
if lut.numParams > 0 and params.len > 0:
|
let opcode = Op(idToOpcode[node.strVal].intVal)
|
||||||
if lut.numParams != params.len:
|
|
||||||
error("Opcode '" & $opcode & "' expect " & $lut.numParams & " params, but got " & $params.len, node)
|
|
||||||
case opcode
|
case opcode
|
||||||
of Push1..Push32:
|
of Push1..Push32:
|
||||||
if params.len != 1:
|
if params.len != 1:
|
||||||
error("expect 1 param, but got " & $params.len, node)
|
error("expect 1 param, but got " & $params.len, node)
|
||||||
let paramWidth = (opcode.ord - 95) * 2
|
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':
|
if val[0] == '0' and val[1] == 'x':
|
||||||
val = val.substr(2)
|
val = val.substr(2)
|
||||||
if val.len != paramWidth:
|
if val.len != paramWidth:
|
||||||
error("expected param with " & $paramWidth & " hex digits, got " & $val.len, node)
|
error("expected param with " & $paramWidth & " hex digits, got " & $val.len, node)
|
||||||
g_asm.code.add byte(opcode)
|
code.add byte(opcode)
|
||||||
g_asm.code.add hexToSeqByte(val)
|
code.add hexToSeqByte(val)
|
||||||
else:
|
else:
|
||||||
error("invalid hex format", node)
|
error("invalid hex format", node)
|
||||||
else:
|
else:
|
||||||
if params.len > 0:
|
if params.len > 0:
|
||||||
for i in countDown(params.len - 1, 0):
|
error("there should be no param for this instruction", node)
|
||||||
g_asm.code.add byte(Push32)
|
code.add byte(opcode)
|
||||||
g_asm.code.add validateVMWord(params[i], node)
|
|
||||||
g_asm.code.add byte(opcode)
|
|
||||||
|
|
||||||
proc addLiteral(node: NimNode, rawCode: string) =
|
proc parseCode(codes: NimNode): seq[byte] =
|
||||||
if rawCode[0] == '0' and rawCode[1] == 'x':
|
let emptyNode = newEmptyNode()
|
||||||
let val = rawCode.substr(2)
|
codes.expectKind nnkStmtList
|
||||||
g_asm.code.add hexToSeqByte(val)
|
var addStop = true
|
||||||
else:
|
for pc, line in codes:
|
||||||
error("invalid hex format", node)
|
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 =
|
proc generateVMProxy(boa: Assembler): NimNode =
|
||||||
let vmProxy = genSym(nskProc, "vmProxy")
|
let vmProxy = genSym(nskProc, "vmProxy")
|
||||||
|
@ -309,54 +259,6 @@ proc generateVMProxy(boa: Assembler): NimNode =
|
||||||
when defined(macro_assembler_debug):
|
when defined(macro_assembler_debug):
|
||||||
echo result.toStrLit.strVal
|
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
|
const
|
||||||
blockFile = "tests" / "fixtures" / "PersistBlockTests" / "block47205.json"
|
blockFile = "tests" / "fixtures" / "PersistBlockTests" / "block47205.json"
|
||||||
|
|
||||||
|
@ -500,36 +402,8 @@ proc runVM*(blockNumber: Uint256, chainDB: BaseChainDB, boa: Assembler): bool =
|
||||||
|
|
||||||
result = true
|
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 =
|
macro assembler*(list: untyped): untyped =
|
||||||
var boa: Assembler
|
var boa = Assembler(success: true)
|
||||||
boa.success = true
|
|
||||||
list.expectKind nnkStmtList
|
list.expectKind nnkStmtList
|
||||||
for callSection in list:
|
for callSection in list:
|
||||||
callSection.expectKind(nnkCall)
|
callSection.expectKind(nnkCall)
|
||||||
|
@ -540,7 +414,7 @@ macro assembler*(list: untyped): untyped =
|
||||||
let title = body[0]
|
let title = body[0]
|
||||||
title.expectKind(nnkStrLit)
|
title.expectKind(nnkStrLit)
|
||||||
boa.title = title.strVal
|
boa.title = title.strVal
|
||||||
of "code" : result = assemblerImpl(boa, body)
|
of "code" : boa.code = parseCode(body)
|
||||||
of "memory": boa.memory = parseVMWords(body)
|
of "memory": boa.memory = parseVMWords(body)
|
||||||
of "stack" : boa.stack = parseVMWords(body)
|
of "stack" : boa.stack = parseVMWords(body)
|
||||||
of "storage": boa.storage = parseStorage(body)
|
of "storage": boa.storage = parseStorage(body)
|
||||||
|
@ -549,4 +423,4 @@ macro assembler*(list: untyped): untyped =
|
||||||
of "data": boa.data = parseData(body)
|
of "data": boa.data = parseData(body)
|
||||||
of "output": boa.output = parseData(body)
|
of "output": boa.output = parseData(body)
|
||||||
else: error("unknown section '" & label & "'", callSection[0])
|
else: error("unknown section '" & label & "'", callSection[0])
|
||||||
g_asm = boa
|
result = boa.generateVMProxy()
|
||||||
|
|
Loading…
Reference in New Issue