Labels as values and threaded-code interpretation
Manu
turkeyman at gmail.com
Fri May 31 22:41:30 PDT 2013
GCC has a non-standard extension to do this, I've used it to get great
performance wins writing emulators. I love this feature, love to see it in
D!
On 1 Jun 2013 15:30, "Alex Rønne Petersen" <alex at lycus.org> wrote:
> Hi,
>
> I'm sure this has been brought up before, but I feel I need to bring it up
> again (because I'm going to be writing a threaded-code interpreter):
> http://gcc.gnu.org/onlinedocs/**gcc/Labels-as-Values.html<http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html>
>
> This is an incredibly important extension. The final switch statement is
> not a replacement because it doesn't allow the programmer to store a label
> address directly into a code stream, which is what's essential to write a
> threaded-code interpreter.
>
> The Erlang folks went through hell just to use this feature; see the 5th Q
> at: http://www.erlang.org/doc/**installation_guide/INSTALL-**
> WIN32.html#Frequently-Asked-**Questions<http://www.erlang.org/doc/installation_guide/INSTALL-WIN32.html#Frequently-Asked-Questions>
>
> The idea is to be able to write code like this:
>
> ----
>
> import std.algorithm;
>
> enum Op : ubyte
> {
> imm,
> add,
> sub,
> // ...
> ret,
> }
>
> final class Insn
> {
> Op op;
> size_t[] args;
> void* lbl;
> Insn next;
> }
>
> final class State
> {
> Insn pc;
> size_t[64] regs;
> }
>
> size_t interp(Insn[] code)
> {
> // Set up the instruction stream with label addresses
> // the first time that it is executed. Label addresses
> // are stable, so we only do this once.
>
> foreach (insn; code.filter!(x => !x.lbl)())
> {
> void* lbl;
>
> with (Op)
> {
> final switch (insn.op)
> {
> case imm: lbl = &&handle_imm; break;
> case add: lbl = &&handle_add; break;
> case sub: lbl = &&handle_sub; break;
> // ...
> case ret: lbl = &&handle_ret; break;
> }
> }
>
> insn.lbl = lbl;
> }
>
> // Start interpreting the entry instruction.
>
> auto state = new State;
> state.pc = code[0];
> goto *state.pc.lbl;
>
> // Interpreter logic follows...
>
> // The whole point is to avoid unnecessary function
> // calls, so we use a mixin template for this stuff.
> enum advance = "state.pc = state.pc.next;" ~
> "goto *state.pc.lbl;";
>
> handle_imm:
> {
> state.regs[state.pc.args[0]] = state.pc.args[1];
> mixin(advance);
> }
>
> handle_add:
> {
> state.regs[state.pc.args[0]] = state.regs[state.pc.args[1]] +
> state.regs[state.pc.args[2]];
> mixin(advance);
> }
>
> handle_sub:
> {
> state.regs[state.pc.args[0]] = state.regs[state.pc.args[1]] -
> state.regs[state.pc.args[2]];
> mixin(advance);
> }
>
> // ...
>
> handle_ret:
> {
> return state.regs[state.pc.args[0]];
> }
>
> assert(false);
> }
>
> ----
>
> Notice that there are no function calls going on. Just plain jumps all the
> way through. This is a technique that many real world interpreters use to
> get significant speedups, and I for one think we desperately need it.
>
> Implementing it should be trivial as both LLVM and GCC support taking the
> address of a block. I'm sure the DMD back end could be extended to support
> it too.
>
> --
> Alex Rønne Petersen
> alex at alexrp.com / alex at lycus.org
> http://alexrp.com / http://lycus.org
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20130601/017364ee/attachment-0001.html>
More information about the Digitalmars-d
mailing list