[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2.3.3 A more complex example, an RPN calculator

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);
  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_movi_i(JIT_R0, atoi(buf));
    } else if (*expr == '+') {
      jit_addr_i(JIT_R0, JIT_R1, JIT_R0);
    } else if (*expr == '-') {
      jit_subr_i(JIT_R0, JIT_R1, JIT_R0);
    } else if (*expr == '*') {
      jit_mulr_i(JIT_R0, JIT_R1, JIT_R0);
    } else if (*expr == '/') {
      jit_divr_i(JIT_R0, JIT_R1, JIT_R0);
    } else {
      fprintf(stderr, "cannot compile: %s\n", expr);
  jit_movr_i(JIT_RET, JIT_R0);
  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;

  c2f = compile_rpn("9*5/32+");
  f2c = compile_rpn("32-5*9/");
  jit_flush_code(codeBuffer, jit_get_ip().ptr);

  for (i = 0; i <= 100; i += 10) printf("%3d ", i);
  for (i = 0; i <= 100; i += 10) printf("%3d ", c2f(i));

  for (i = 32; i <= 212; i += 10) printf("%3d ", i);
  for (i = 32; i <= 212; i += 10) printf("%3d ", f2c(i));
  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] [ ? ]

This document was generated by Alistair Turnbull on April, 12 2005 using texi2html