One of the things that my compiler frontend has is an interpreter, which allows fairly elaborate constant expressions to be folded into simple constants. This is particularly useful in the case of annotations that take arguments - rather than executing a static constructor before main(), I allow the constructor to run at compile time (if it can), generating a constant struct with all of it’s members pre-filled in.
Currently this is implemented as a tree-walk on my high-level CFG. However, it would simplify things if I could instead interpret the generated LLVM IR. For one thing, this would allow the compiler to import class definitions directly from bitcode files. (I already serialize the high-level type information as metadata nodes, but I’d like to avoid serializing the high-level function bodies as well.)
Rather than writing my own interpreter for LLVM IR, I’d like to investigate the possibility of using the interpreter facilities of ExecutionEngine. I definitely don’t want to use the JIT, as these methods are probably only going to be executed once.
There are a number of issues to be resolved before this can work. The biggest is that the compile-time environment and data types are totally different from their runtime counterparts. In the compiler, a struct instance is represented as an array of constant expression nodes, rather than as a flat region of memory. I’d need to redefine the GEP, load, and store operations of the interpreter at a very minimum.
I’m also assuming that the interpreter would require all input values to be converted into IR Value* types - I wouldn’t expect the interpreter to be able to operate on my frontend’s Expr* nodes. But that means that I would need a method for converting Value* constants back into Expr* nodes after the execution was complete. Since I have complete static type information for the output value, this isn’t as onerous as it sounds.
I’d also need some way to prevent the interpreter from going into an infinite loop, or infinite recursion - probably a hard-coded limit on the number of instructions executed and call-frame depth.
Also, looking in the SVN repo, it appears that some of the files in include/llvm/ExecutionEngine have not been touched in a long time, and that scares me a bit - what’s the current state of the LLVM interpreter?
Anyway, my main question to the list is - does this sound like a viable plan, or am I heading down the path of madness and frustration?