Related articles |
---|
ultra fast but too complex interpreter. hoesel@igc.ethz.ch (1993-03-17) |
ultra fast but too complex ... summary hoesel@igc.ethz.ch (1993-03-22) |
Re: ultra fast but too complex ... summary fjh@mundil.cs.mu.OZ.AU (1993-03-23) |
Newsgroups: | comp.compilers |
From: | fjh@mundil.cs.mu.OZ.AU (Fergus James HENDERSON) |
Keywords: | interpreter |
Organization: | Computer Science, University of Melbourne, Australia |
References: | 93-03-060 93-03-080 |
Date: | Tue, 23 Mar 1993 13:26:36 GMT |
hoesel@igc.ethz.ch (Frans van Hoesel) writes:
>My conclusions:
>gnu-cc is faster for both the all approaches (it has global registers, and
>values-as-labels), but the features needed from gnu-c are *NOT* ansi.
>Well, how cares you could say, almost any machine has gnu-c... forget
>about ansi. I won't! I need optimizing compilers, and vectorrizing
>compilers to handle the primitive functions that will handle my vector
>data (those functions would be highly optimized) threading is only really
>fast in non-standard environments. I like the concept very much, but it
>will not work in ansi.
I happen to be writing an interpreter at the moment. For simplicity we
decided to use a stack-based bytecode, at least for the first
implementation. However that still leaves a lot of implementation
choices, eg. ie. do you use a switch statement, or do you use gcc's
labels-as-values extension, or alterately do you make each the code to
interpret each instruction a separate function, etc.
What we have done is to use a little bit of C macro-hackery to enable us
to easily code up all of these implementation strategies and select
between them by means of conditional compilation. This means that we can
actually run performance comparisons between the different versions,
instead of just guessing at the beginning of the project which method to
use, and being forced into a major rewrite if we decide to use a different
method.
So what I would suggest to you is *don't* decide which method you will
use, until *after* you have implemented several different methods and
compared the performance of each method. (You could even have the Makefile
for your interpreter automatically generate several different versions,
test their efficiency, and then automatically install the fastest one. :-)
A description of the C macro-hackery follows; skip it if you're not
interested.
Basically it works like this: all the code to interpret each
opcode is defined in a file 'opcodes.def' like this:
OPCODE(POP,
/* the code to interpret POP goes here */
)
OPCODE(PUSH,
/* the code to interpret PUSH goes here */
)
...
By #including this file with different definitions for the OPCODE macro,
you can generate interpreters using a variety of different implementation
strategies. For example,
interpret(...some parameters...)
{
... declarations for your interpreter's registers ...
#ifdef __GNUC__
/* Use Gnu C's "labels-as-values" extension */
/* We use a 'paste' macro (which can be defined using ANSI C's ## operator)
to generate labels INTERPRET_PUSH, INTERPRET_POP, etc. */
static void *interpret_op[] = {
#define OPCODE(opcode,code) &&paste(INTERPRET_,opcode),
#include "opcodes.def"
#undef OPCODE
};
#endif
... initialize the registers ...
#ifdef __GNUC__
/* Use Gnu C's "labels-as-values" extension */
goto *interpret_op[*reg_ip++];
#define OPCODE(opcode,code) \
paste(INTERPRET_,opcode): \
{ code } \
goto *interpret_op[*reg_ip++];
#include "opcodes.def"
#undef OPCODE
#else /* __GNUC__ */
/* We can't use any gcc extensions.
Use a big switch statement instead. */
while(1)
switch (*reg_ip++)
{
/* We use opcodes.def to build the different cases
for the switch statement */
#define OPCODE(opcode,operand_type,code) \
case opcode: \
{ code } \
break;
#include "opcodes.def"
#undef OPCODE
}
#endif /* __GNUC__ */
}
--
Fergus Henderson fjh@munta.cs.mu.OZ.AU
--
Return to the
comp.compilers page.
Search the
comp.compilers archives again.