By using function pointers!

The concept of "function" or "procedure" doesn't really exist concretely. Sure, some processors provide opcodes to view the accessible memory as a "stack", to accommodate some specific, if popular, block structure implementations, namely C and Fortran's.

The most critical part is mapping the language's concept of "function", an executable body of code where all the referenced variables have specific instances/allocation, reachable at the time of its invocation, with the processor's, or most likely, C's view of how the processor underneath haphazardly implements block structure.

If your JIT compiler can seal a body of code and guarantee to implement the environment (the mapping from address/name to value) in away accessible to the dumb processor, then you will win.

This requires that the compiler hacker implement code to convert values from HLL types, to lower level ones. And convert the implementation of closures and environment, to something more palatable to the lower implementations.

Below is a simple "JIT" interpreter. It uses pre-compiled C code in a shared library, along with its own "natively" compiled operations. A real JIT compiler following this protocol would just make sure it's able to find the functions by any other means, other than compile-time name lookup, as this one does. It can maintain its own explicit symbol table, hashing a function name along with type signature, and dispatch on it at run time.

In the code below, ADD and SUB are the usual addition and subtraction operations, precompiled "for speed", while DIV and MUL are "interpreted" byte-codes. In the current design, both types of functions (native and bytecode) are mapped into a single namespace; typically, there are predicates to distinguish between the two. In most Common Lisp implementations, the two co-exist side by side, and you can call and interpreted function just as you can call a native one.

This is just a quick 1-hour hack, let me know if it's not clear.

Here is the runtime library.

/* --- compiled-functions.c */

/* Compile with: gcc -fPIC -g -c compiled-functions.c */

extern int add (int x, int y);
extern int sub (int x, int y);

int add (int x, int y) {
return x + y;
}

int sub (int x, int y) {
return x - y;
}

Here is the JIT compiler:

/* jit-test.c main driver */
/* compile as gcc jit-test.c compiled-functions.o */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* library functions */
extern int add (int x, int y);
extern int sub (int x, int y);

/* compiled opcode values */
#define ADD 0
#define SUB 1
#define MUL 2
#define DIV 3

/* string opcode names */
char *opcode_names[] = {"ADD", "SUB", "MUL", "DIV"};

#define OPCODE_COUNT 4

/* the values returned by the parser */
struct input {
int op, arg1, arg2;
};

/* rudimentary parser, converts strings to internal structure. */
struct input *parse_input (char *op_name, char *arg1, char *arg2) {
int i, opcode = -1;
struct input *parsed_input = (struct input *) malloc (sizeof (struct input));

/* convert opcode name to opcode value */
for (i = 0; i < OPCODE_COUNT; ++i) {
if (strcmp (op_name, opcode_names[i]) == 0)
opcode = i;
}

if (opcode >= 0) {
parsed_input->op = opcode;
parsed_input->arg1 = atoi(arg1);
parsed_input->arg2 = atoi(arg2);
}
return parsed_input;

}

int main (int argc, char **argv) {
int result;

/* this points to an unknow jit function linked against us */
int (* jit_fn) (int, int); /* <--------- Function pointer! */

struct input *input = parse_input ("DIV", "4", "3");

if(input) {
printf ("%s %d %d ==> ", opcode_names[input->op], input->arg1, input->arg2);
}

switch (input->op) {
case ADD:
jit_fn = &add;
result = jit_fn (input->arg1, input->arg2);
break;
case SUB:
jit_fn = &sub;
result = jit_fn (input->arg1, input->arg2);
break;
case MUL:
result = (input->arg1 * input->arg2);
break;
case DIV:
result = (input->arg1 / input->arg2);
break;
}

printf ("%d\n", result);

return 0;

}