Thanks for the suggestions, everyone. The motive for this is essentially that I'd been playing around with designing interpreters for various Turing-incomplete programming languages, and I kept having to design and write new abstract machines, which seemed like a lot of wasted effort. And JIT seemed especially nice to have, since it would reduce the interpretation overhead, although it isn't strictly necessary. As Kamil suggests, I think that being able to have a portable bytecode interpreter may be key, since then the JIT can simply act as an optimisation for certain supported platforms, and then it won't tie the program to any particular architecture.
So I think the closest to what I'm looking for might be Dis and QBE. My problems with Dis are that it has a lot of higher-level features (due to Limbo), and that it assumes garbage collection. A subset might work, but since it's unsupported now anyway it might be worth just learning from it, instead of actually adopting it. Lua has similar problems with high-level language features and garbage collection, neither of which fit in C without an additional object model on top, which I'd like to avoid. QBE seems like it could be good, although I couldn't tell when I looked whether the intermediate language is structured or a bytecode. In any case, I think the way it's designed rules out a portable C interpreter, since it has no pointer type, for instance. It might be relatively easy to adapt to the task though. I'll have to give it some more thought. Thanks again, cls