jitters: jit: add JIT function
This commit is contained in:
parent
938dddc43a
commit
1a1148b2b2
190
src/jit/jitter.c
Normal file
190
src/jit/jitter.c
Normal file
|
@ -0,0 +1,190 @@
|
|||
#include "jitter.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
static void jit_ast_internal(const struct ast_node *ast, struct vector *buf);
|
||||
|
||||
static void jit_num(int num, struct vector *buf)
|
||||
{
|
||||
// movl $num, %eax
|
||||
// 08 NUM's value
|
||||
append_vector(buf, 0xb8);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
const unsigned char c = num & 0xff;
|
||||
num >>= 8;
|
||||
append_vector(buf, c);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_division(const struct binop_node *bin_op, struct vector *buf)
|
||||
{
|
||||
const unsigned char handle_rhs[] = {
|
||||
0x48, 0x83, 0xec, 0x04, // sub $0x4,%rsp
|
||||
0x89, 0x04, 0x24, // mov %eax,(%rsp)
|
||||
};
|
||||
jit_ast_internal(bin_op->rhs, buf);
|
||||
append_array(buf, handle_rhs, ARR_SIZE(handle_rhs));
|
||||
|
||||
const unsigned char handle_lhs[] = {
|
||||
0x8b, 0x0c, 0x24, // mov (%rsp),%ecx
|
||||
0x48, 0x83, 0xc4, 0x04, // add $0x4,%rsp
|
||||
};
|
||||
jit_ast_internal(bin_op->lhs, buf);
|
||||
append_array(buf, handle_lhs, ARR_SIZE(handle_lhs));
|
||||
|
||||
const unsigned char handle_div[] = {
|
||||
0x99, // cltd
|
||||
0xf7, 0xf9, // idiv %ecx
|
||||
};
|
||||
append_array(buf, handle_div, ARR_SIZE(handle_div));
|
||||
}
|
||||
|
||||
static void jit_binop(const struct binop_node* bin_op, struct vector *buf)
|
||||
{
|
||||
|
||||
if (bin_op->op == DIVIDE)
|
||||
{
|
||||
handle_division(bin_op, buf);
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned char handle_lhs[] = {
|
||||
0x48, 0x83, 0xec, 0x04, // sub $0x4,%rsp
|
||||
0x89, 0x04, 0x24, // mov %eax,(%rsp)
|
||||
};
|
||||
jit_ast_internal(bin_op->lhs, buf);
|
||||
append_array(buf, handle_lhs, ARR_SIZE(handle_lhs));
|
||||
|
||||
const unsigned char handle_rhs[] = {
|
||||
0x8b, 0x3c, 0x24, // mov (%rsp),%edi
|
||||
0x48, 0x83, 0xc4, 0x04, // add $0x4,%rsp
|
||||
};
|
||||
jit_ast_internal(bin_op->rhs, buf);
|
||||
append_array(buf, handle_rhs, ARR_SIZE(handle_rhs));
|
||||
|
||||
const unsigned char handle_plus[] = {
|
||||
0x01, 0xf8, // add %edi,%eax
|
||||
};
|
||||
const unsigned char handle_minus[] = {
|
||||
0x29, 0xf8, // sub %edi,%eax
|
||||
};
|
||||
const unsigned char handle_times[] = {
|
||||
0x0f, 0xaf, 0xc7, // imul %edi,%eax
|
||||
};
|
||||
switch (bin_op->op)
|
||||
{
|
||||
case PLUS:
|
||||
append_array(buf, handle_plus, ARR_SIZE(handle_plus));
|
||||
break;
|
||||
case MINUS:
|
||||
append_array(buf, handle_minus, ARR_SIZE(handle_minus));
|
||||
break;
|
||||
case TIMES:
|
||||
append_array(buf, handle_times, ARR_SIZE(handle_times));
|
||||
break;
|
||||
case DIVIDE:
|
||||
default:
|
||||
/* Not handled */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void jit_unop(const struct unop_node* un_op, struct vector *buf)
|
||||
{
|
||||
jit_ast_internal(un_op->rhs, buf);
|
||||
|
||||
const unsigned char handle_negate[] = {
|
||||
0x6b, 0xc0, 0xff // imul $-1,%eax,%eax
|
||||
};
|
||||
switch (un_op->op)
|
||||
{
|
||||
case NEGATE:
|
||||
append_array(buf, handle_negate, ARR_SIZE(handle_negate));
|
||||
break;
|
||||
case IDENTITY:
|
||||
default:
|
||||
/* Nothing to do */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void jit_ast_internal(const struct ast_node *ast, struct vector *buf)
|
||||
{
|
||||
switch (ast->kind)
|
||||
{
|
||||
case BINOP:
|
||||
jit_binop(&ast->val.bin_op, buf);
|
||||
break;
|
||||
case UNOP:
|
||||
jit_unop(&ast->val.un_op, buf);
|
||||
break;
|
||||
case NUM:
|
||||
jit_num(ast->val.num, buf);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void jit_ast(const struct ast_node *ast, struct vector *buf)
|
||||
{
|
||||
const unsigned char prologue[] = {
|
||||
0x55, // push %rbp
|
||||
0x48, 0x89, 0xe5, // mov %rsp,%rbp
|
||||
};
|
||||
append_array(buf, prologue, ARR_SIZE(prologue));
|
||||
|
||||
jit_ast_internal(ast, buf);
|
||||
|
||||
const unsigned char epilogue[] = {
|
||||
0x5d, // pop %rbp
|
||||
0xc3, // retq
|
||||
};
|
||||
append_array(buf, epilogue, ARR_SIZE(epilogue));
|
||||
}
|
||||
|
||||
#define WRITE_PROT (PROT_READ | PROT_WRITE)
|
||||
#define WRITE_FLAGS (MAP_ANONYMOUS | MAP_PRIVATE)
|
||||
static int exec_buf(const void *buf, size_t len)
|
||||
{
|
||||
// Copy our JIT-ed instructions into a new buffer
|
||||
void *ptr = mmap(NULL, len, WRITE_PROT, WRITE_FLAGS, 0, 0);
|
||||
if (ptr == MAP_FAILED)
|
||||
err(1, NULL);
|
||||
memcpy(ptr, buf, len);
|
||||
|
||||
// Make the JIT-ed function executable
|
||||
if (mprotect(ptr, len, PROT_READ | PROT_EXEC) < 0)
|
||||
err(1, NULL);
|
||||
|
||||
// Cast our pointer into a function pointer
|
||||
// Thanks to (-Wpedantic) we need to do this pragma song and dance...
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
int (*func)(void) = ptr;
|
||||
#pragma GCC diagnostic pop
|
||||
// Call the JIT-ed function
|
||||
int ret = func();
|
||||
|
||||
// Un-map the JIT-ed function
|
||||
if (munmap(ptr, len) < 0)
|
||||
err(1, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int jit_eval_ast(const struct ast_node *ast)
|
||||
{
|
||||
struct vector *buf = create_vector(1);
|
||||
jit_ast(ast, buf);
|
||||
|
||||
int ret = exec_buf(buf->buf, buf->size);
|
||||
|
||||
destroy_vector(buf);
|
||||
return ret;
|
||||
}
|
8
src/jit/jitter.h
Normal file
8
src/jit/jitter.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef JITTER_H
|
||||
#define JITTER_H
|
||||
|
||||
#include "ast/ast.h"
|
||||
|
||||
int jit_eval_ast(const struct ast_node *ast);
|
||||
|
||||
#endif /* !JITTER_H */
|
5
src/jit/local.am
Normal file
5
src/jit/local.am
Normal file
|
@ -0,0 +1,5 @@
|
|||
jitters_SOURCES += \
|
||||
%D%/jitter.c \
|
||||
%D%/jitter.h \
|
||||
%D%/vector.h \
|
||||
$(NULL)
|
74
src/jit/vector.h
Normal file
74
src/jit/vector.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
#ifndef VECTOR_H
|
||||
#define VECTOR_H
|
||||
|
||||
#include <err.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct vector
|
||||
{
|
||||
unsigned char *buf;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
static inline void *xmalloc(size_t size)
|
||||
{
|
||||
void *ret = malloc(size);
|
||||
|
||||
if (ret == NULL)
|
||||
err(1, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void *xrealloc(void *ptr, size_t size)
|
||||
{
|
||||
void *ret = realloc(ptr, size);
|
||||
|
||||
if (ret == NULL)
|
||||
{
|
||||
free(ptr);
|
||||
err(1, NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline struct vector *create_vector(size_t init_cap)
|
||||
{
|
||||
struct vector *vec = xmalloc(sizeof(*vec));
|
||||
|
||||
init_cap = (init_cap ? init_cap : 1);
|
||||
|
||||
vec->buf = xmalloc(init_cap);
|
||||
vec->size = 0;
|
||||
vec->capacity = init_cap;
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
static inline void destroy_vector(struct vector *vec)
|
||||
{
|
||||
free(vec->buf);
|
||||
free(vec);
|
||||
}
|
||||
|
||||
static inline void append_vector(struct vector *vec, unsigned char c)
|
||||
{
|
||||
if (vec->size >= vec->capacity)
|
||||
vec->buf = xrealloc(vec->buf, vec->capacity *= 2);
|
||||
|
||||
vec->buf[vec->size++] = c;
|
||||
}
|
||||
|
||||
#define ARR_SIZE(ARR) (sizeof((ARR)) / sizeof((ARR)[0]))
|
||||
|
||||
static inline void append_array(
|
||||
struct vector *buf, const unsigned char *arr, size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
append_vector(buf, arr[i]);
|
||||
}
|
||||
|
||||
#endif /* !VECTOR_H */
|
|
@ -10,6 +10,7 @@
|
|||
#include "ast/ast.h"
|
||||
#include "compile/compiler.h"
|
||||
#include "eval/evaluator.h"
|
||||
#include "jit/jitter.h"
|
||||
#include "parse/parse-jitters.h"
|
||||
#include "print/printer.h"
|
||||
|
||||
|
@ -21,11 +22,12 @@ static char doc[] =
|
|||
|
||||
|
||||
static struct argp_option options[] = {
|
||||
{"evaluate", 'e', 0, 0, "Evaluate input by walking the tree" },
|
||||
{"compile", 'c', 0, 0, "Compile input to assembly" },
|
||||
{"print", 'p', 0, 0, "Print parsed expression" },
|
||||
{"output", 'o', "FILE", 0, "Output to FILE instead of standard output" },
|
||||
{"debug", 'd', 0, 0, "Emit debug output from parser" },
|
||||
{"evaluate", 'e', 0, 0, "Evaluate input by walking the tree", 0 },
|
||||
{"compile", 'c', 0, 0, "Compile input to assembly", 0 },
|
||||
{"jit", 'j', 0, 0, "JIT-compile input and evaluate", 0 },
|
||||
{"print", 'p', 0, 0, "Print parsed expression", 0 },
|
||||
{"output", 'o', "FILE", 0, "Output to FILE instead of standard output", 0 },
|
||||
{"debug", 'd', 0, 0, "Emit debug output from parser", 0 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
@ -33,6 +35,7 @@ struct arguments
|
|||
{
|
||||
bool debug; // Whether to activate Bison using debug trace
|
||||
bool compile; // Whether to compile the input
|
||||
bool jit; // Whether to JIT the input
|
||||
bool evaluate; // Whether to evaluate the input
|
||||
bool print; // Whether to print the input
|
||||
const char *output_file; // Where to output
|
||||
|
@ -53,6 +56,9 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
|
|||
case 'e':
|
||||
arguments->evaluate = true;
|
||||
break;
|
||||
case 'j':
|
||||
arguments->jit = true;
|
||||
break;
|
||||
case 'p':
|
||||
arguments->print = true;
|
||||
break;
|
||||
|
@ -70,7 +76,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct argp argp = { options, parse_opt, 0, doc };
|
||||
static struct argp argp = { options, parse_opt, 0, doc, 0, 0, 0, };
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
@ -91,6 +97,8 @@ int main(int argc, char *argv[])
|
|||
if ((ret = yyparse(&ast)) == 0) {
|
||||
if (arguments.compile)
|
||||
compile_ast(ast, output);
|
||||
if (arguments.jit)
|
||||
fprintf(output, "%d\n", jit_eval_ast(ast));
|
||||
if (arguments.evaluate)
|
||||
fprintf(output, "%d\n", evaluate_ast(ast));
|
||||
if (arguments.print)
|
||||
|
|
|
@ -17,5 +17,6 @@ jitters_LDADD =
|
|||
include %D%/ast/local.am
|
||||
include %D%/compile/local.am
|
||||
include %D%/eval/local.am
|
||||
include %D%/jit/local.am
|
||||
include %D%/parse/local.am
|
||||
include %D%/print/local.am
|
||||
|
|
Loading…
Reference in a new issue