/* * Copyright (c) 2004 Sergey Lyubka * * Simple example of just-in-time (JIT) compilation. * Program accepts single argument, which should be arithmetic expression * in reverse Polish notation, for example: ./a.out "44 77 + 2 *" * Expression then compiled into the x86 function and called. */ #include #include #include #include #include /* Compilation unit */ struct cu { unsigned char code[512]; /* Generated machine code */ int pc; /* Program counter */ int stk; /* Number of objects in stack */ int err; /* Error */ }; enum { ERR_OK, /* No error */ ERR_STKOVER, /* Stack overflow */ ERR_STKUNDER /* Stack underflow */ }; static void elog(int fatal, const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void) vfprintf(stderr, fmt, ap); va_end(ap); (void) fputc('\n', stderr); if (fatal) exit(EXIT_FAILURE); } /* * End function: * Put value from stack into EAX. * It should be just one value on stack */ static void leave(struct cu *cup) { if (cup->stk > 1) cup->err = ERR_STKUNDER; else if (cup->stk < 1) cup->err = ERR_STKOVER; else { cup->code[cup->pc++] = 0x58; /* POP EAX */ cup->code[cup->pc++] = 0xc3; /* RET */ } elog(0, "%s", __FUNCTION__); } /* * Enter a function */ static void enter(struct cu *cup) { cup = NULL; elog(0, "%s", __FUNCTION__); } /* * Push operand on stack */ static void push(struct cu *cup, int value) { cup->code[cup->pc++] = 0x68; /* PUSH */ (void) memcpy(&cup->code[cup->pc], &value, 4); cup->pc += 4; /* Increment number of items on stack */ cup->stk++; elog(0, "%s: %d", __FUNCTION__, value); } /* * Add two numbers on stack */ static void add(struct cu *cup) { if (cup->stk < 2) { cup->err = ERR_STKOVER; } else { cup->code[cup->pc++] = 0x58; /* POP EAX */ cup->code[cup->pc++] = 0x59; /* POP ECX */ cup->code[cup->pc++] = 0x01; /* ADD EAX, ECX */ cup->code[cup->pc++] = 0xc8; cup->code[cup->pc++] = 0x50; /* PUSH EAX*/ cup->stk--; } elog(0, "%s", __FUNCTION__); } /* * Substitute numbers */ static void sub(struct cu *cup) { if (cup->stk < 2) { cup->err = ERR_STKOVER; } else { cup->code[cup->pc++] = 0x59; /* POP ECX */ cup->code[cup->pc++] = 0x58; /* POP EAX */ cup->code[cup->pc++] = 0x29; /* SUB EAX, ECX */ cup->code[cup->pc++] = 0xc8; cup->code[cup->pc++] = 0x50; /* PUSH EAX*/ cup->stk--; } elog(0, "%s", __FUNCTION__); } /* * Divide numbers */ static void idiv(struct cu *cup) { if (cup->stk < 2) { cup->err = ERR_STKOVER; } else { cup->code[cup->pc++] = 0x59; /* POP ECX */ cup->code[cup->pc++] = 0x58; /* POP EAX */ cup->code[cup->pc++] = 0xf7; /* DIV ECX */ cup->code[cup->pc++] = 0xf1; cup->code[cup->pc++] = 0x50; /* PUSH EAX*/ cup->stk--; } elog(0, "%s", __FUNCTION__); } /* * Multiply two numbers on stack */ static void mul(struct cu *cup) { if (cup->stk < 2) { cup->err = ERR_STKOVER; } else { cup->code[cup->pc++] = 0x58; /* POP EAX */ cup->code[cup->pc++] = 0x59; /* POP ECX */ cup->code[cup->pc++] = 0xf7; /* MUL ECX */ cup->code[cup->pc++] = 0xe1; cup->code[cup->pc++] = 0x50; /* PUSH EAX */ cup->stk--; } elog(0, "%s", __FUNCTION__); } /* * Compile given text */ static void compile(struct cu *cup, const char *text) { unsigned state, number, i = 0; /* Initialize compilation unit structure */ (void) memset(cup, 0, sizeof(*cup)); enter(cup); /* Parse text */ for (state = 0; ; text++, i++) { switch (*text) { case '+': add(cup); break; case '*': mul(cup); break; case '/': idiv(cup); break; case '-': sub(cup); break; case '\0': case ' ': case '\t': if (state == 1) push(cup, number); if (*text == '\0') leave(cup); /* End of text, return */ state = 0; break; default: if (isdigit(*text)) { if (state == 0) number = 0; number *= 10; number += *text - '0'; state = 1; } else { elog(1, "compile: error: [%s]", text); } break; } if (cup->err != ERR_OK) elog(1, "compile: error: [%s]: %d", text, cup->err); if (*text == '\0') break; } } int main(int argc, char *argv[]) { struct cu cu; int result = 0; /* * we need this to avoid compiler message * ISO C forbids conversion of object pointer to function pointer type * - denk */ union { int (*func)(void); void *proc; } code; if (argc < 2) { (void) fprintf(stderr, "usage: %s \"\"\n", argv[0]); exit(EXIT_FAILURE); } else { compile(&cu, argv[1]); /* Call generated code */ /* result = ((int (*)(void)) cu.code)(); */ code.proc = &cu.code; result = (*code.func)(); printf("result: %d\n", result); } return (EXIT_SUCCESS); }