| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
We create a small stack-based RPN calculator which applies a series of operators to a given parameter and to other numeric operands. Unlike previous examples, the code generator is fully parameterized and is able to compile different formulas to different functions. Here is the code for the expression compiler; a sample usage will follow.
#include <stdio.h>
#include "lightning.h"
typedef int (*pifi)(int); /* Pointer to Int Function of Int */
pifi compile_rpn(char *expr)
{
pifi fn;
int in;
fn = (pifi) (jit_get_ip().iptr);
jit_leaf(1);
in = jit_arg_i();
jit_getarg_i(JIT_R0, in);
while (*expr) {
char buf[32];
int n;
if (sscanf(expr, "%[0-9]%n", buf, &n)) {
expr += n - 1;
jit_push_i(JIT_R0);
jit_movi_i(JIT_R0, atoi(buf));
} else if (*expr == '+') {
jit_pop_i(JIT_R1);
jit_addr_i(JIT_R0, JIT_R1, JIT_R0);
} else if (*expr == '-') {
jit_pop_i(JIT_R1);
jit_subr_i(JIT_R0, JIT_R1, JIT_R0);
} else if (*expr == '*') {
jit_pop_i(JIT_R1);
jit_mulr_i(JIT_R0, JIT_R1, JIT_R0);
} else if (*expr == '/') {
jit_pop_i(JIT_R1);
jit_divr_i(JIT_R0, JIT_R1, JIT_R0);
} else {
fprintf(stderr, "cannot compile: %s\n", expr);
abort();
}
++expr;
}
jit_movr_i(JIT_RET, JIT_R0);
jit_ret();
return fn;
}
|
The principle on which the calculator is based is easy: the stack top is held in R0, while the remaining items of the stack are held on the hardware stack. Compiling an operand pushes the old stack top onto the stack and moves the operand into R0; compiling an operator pops the second operand off the stack into R1, and compiles the operation so that the result goes into R0, thus becoming the new stack top.
Try to locate a call to jit_set_ip in the source code. You
will not find one; this means that the client has to manually set
the instruction pointer. This technique has one advantage and one
drawback. The advantage is that the client can simply set the
instruction pointer once and then generate code for multiple functions,
one after another, without caring about passing a different instruction
pointer each time; see Re-entrant usage of GNU lightning for the disadvantage.
Source code for the client (which lies in the same source file) follows:
static jit_insn codeBuffer[1024];
int main()
{
pifi c2f, f2c;
int i;
jit_set_ip(codeBuffer);
c2f = compile_rpn("9*5/32+");
f2c = compile_rpn("32-5*9/");
jit_flush_code(codeBuffer, jit_get_ip().ptr);
printf("\nC:");
for (i = 0; i <= 100; i += 10) printf("%3d ", i);
printf("\nF:");
for (i = 0; i <= 100; i += 10) printf("%3d ", c2f(i));
printf("\n");
printf("\nF:");
for (i = 32; i <= 212; i += 10) printf("%3d ", i);
printf("\nC:");
for (i = 32; i <= 212; i += 10) printf("%3d ", f2c(i));
printf("\n");
return 0;
}
|
The client displays a conversion table between Celsius and Fahrenheit degrees (both Celsius-to-Fahrenheit and Fahrenheit-to-Celsius). The formulas are, and , respectively.
Providing the formula as an argument to compile_rpn effectively
parameterizes code generation, making it possible to use the same code
to compile different functions; this is what makes dynamic code
generation so powerful.
The `rpn.c' file in the GNU lightning distribution includes a more
complete (and more complex) implementation of compile_rpn,
which does constant folding, allows the argument to the functions
to be used more than once, and is able to assemble instructions with
an immediate parameter.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |