I’m working on a VM, of the classic dispatch-on-value sort, and the last step (which I’m bearing down on) will be to make it type stable. The current implementation uses a multimethod for dispatch, but the Vector holding instructions is boxed and the type is resolved at runtime. I believe I have a handle on how to get all of that working, this is just by way of introduction.
The current runvm!
function is very small, and I have two of them currently, and plan to add a third. There’s no getting around unrolling the dispatch, which I would then have to repeat and keep in sync between all VM instances.
This strikes me as a good candidate for a macro. I can assure that each opcode is compared against in ascending order, to help LLVM emit it as a computed goto, it will always include every opcode, if any of the functions are missing I’ll get an immediate error, and I can just call @dispatchinstruction
, meaning that the function body stays the same size as it is now.
I’ve implemented this macro successfully, but wow, does it not follow recommended style. It has both Meta.parse
and eval
in it, I ended up resorting to building up strings to encode an Expr. Reason being that I couldn’t find a way to express part of an if statement as an expression, which makes sense, because it’s not, it’s just a fragment of one.
Stripped of irrelevancies it looks like this:
macro dispatchinstruction()
fragments = [":("]
first = true
for opcode in instances(Opcode)
opname = "MyModule." * string(opcode)
if first
first = false
push!(fragments, """if \$(esc(:inst)).op == \$(esc($opname))\n""")
else
push!(fragments, """elseif \$(esc(:inst)).op == \$(esc($opname))\n""")
end
fname = "on" * string(opcode)
push!(fragments, """ $fname(\$(esc(:inst)),\$(esc(:vm))) """)
end
push!(fragments, "else error(\"illegal value\")\nend)\n")
eval(Meta.parse(join(fragments)))
end
Which does the job, no complaints. But it leaves me wondering if I missed a trick, given the emphasis placed on not using eval in macros, and building expressions rather than doing string-based codegen, which I’ve seen in both the Julia manual and here on the discourse.
I’m not displeased with the result and am content to ship it as-is, but does anyone see a way to do this without building up a program string and evaluating it? In Common Lisp it would be easy enough to write this macro without strings, and I have a nagging feeling that this is possible in Julia as well.