| /* A compiler for the "bf" language. */ |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #include "libgccjit.h" |
| |
| /* Make "main" function: |
| int |
| main (int argc, char **argv) |
| { |
| ... |
| } |
| */ |
| static gcc_jit_function * |
| make_main (gcc_jit_context *ctxt) |
| { |
| gcc_jit_type *int_type = |
| gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); |
| gcc_jit_param *param_argc = |
| gcc_jit_context_new_param (ctxt, NULL, int_type, "argc"); |
| gcc_jit_type *char_ptr_ptr_type = |
| gcc_jit_type_get_pointer ( |
| gcc_jit_type_get_pointer ( |
| gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_CHAR))); |
| gcc_jit_param *param_argv = |
| gcc_jit_context_new_param (ctxt, NULL, char_ptr_ptr_type, "argv"); |
| gcc_jit_param *params[2] = {param_argc, param_argv}; |
| gcc_jit_function *func_main = |
| gcc_jit_context_new_function (ctxt, NULL, |
| GCC_JIT_FUNCTION_EXPORTED, |
| int_type, |
| "main", |
| 2, params, |
| 0); |
| return func_main; |
| } |
| |
| #define MAX_OPEN_PARENS 16 |
| |
| typedef struct bf_compiler |
| { |
| const char *filename; |
| int line; |
| int column; |
| |
| gcc_jit_context *ctxt; |
| |
| gcc_jit_type *void_type; |
| gcc_jit_type *int_type; |
| gcc_jit_type *byte_type; |
| gcc_jit_type *array_type; |
| |
| gcc_jit_function *func_getchar; |
| gcc_jit_function *func_putchar; |
| |
| gcc_jit_function *func; |
| gcc_jit_block *curblock; |
| |
| gcc_jit_rvalue *int_zero; |
| gcc_jit_rvalue *int_one; |
| gcc_jit_rvalue *byte_zero; |
| gcc_jit_rvalue *byte_one; |
| gcc_jit_lvalue *data_cells; |
| gcc_jit_lvalue *idx; |
| |
| int num_open_parens; |
| gcc_jit_block *paren_test[MAX_OPEN_PARENS]; |
| gcc_jit_block *paren_body[MAX_OPEN_PARENS]; |
| gcc_jit_block *paren_after[MAX_OPEN_PARENS]; |
| |
| } bf_compiler; |
| |
| /* Bail out, with a message on stderr. */ |
| |
| static void |
| fatal_error (bf_compiler *bfc, const char *msg) |
| { |
| fprintf (stderr, |
| "%s:%i:%i: %s", |
| bfc->filename, bfc->line, bfc->column, msg); |
| abort (); |
| } |
| |
| /* Get "data_cells[idx]" as an lvalue. */ |
| |
| static gcc_jit_lvalue * |
| bf_get_current_data (bf_compiler *bfc, gcc_jit_location *loc) |
| { |
| return gcc_jit_context_new_array_access ( |
| bfc->ctxt, |
| loc, |
| gcc_jit_lvalue_as_rvalue (bfc->data_cells), |
| gcc_jit_lvalue_as_rvalue (bfc->idx)); |
| } |
| |
| /* Get "data_cells[idx] == 0" as a boolean rvalue. */ |
| |
| static gcc_jit_rvalue * |
| bf_current_data_is_zero (bf_compiler *bfc, gcc_jit_location *loc) |
| { |
| return gcc_jit_context_new_comparison ( |
| bfc->ctxt, |
| loc, |
| GCC_JIT_COMPARISON_EQ, |
| gcc_jit_lvalue_as_rvalue (bf_get_current_data (bfc, loc)), |
| bfc->byte_zero); |
| } |
| |
| /* Compile one bf character. */ |
| |
| static void |
| bf_compile_char (bf_compiler *bfc, |
| unsigned char ch) |
| { |
| gcc_jit_location *loc = |
| gcc_jit_context_new_location (bfc->ctxt, |
| bfc->filename, |
| bfc->line, |
| bfc->column); |
| |
| /* Turn this on to trace execution, by injecting putchar () |
| of each source char. */ |
| if (0) |
| { |
| gcc_jit_rvalue *arg = |
| gcc_jit_context_new_rvalue_from_int ( |
| bfc->ctxt, |
| bfc->int_type, |
| ch); |
| gcc_jit_rvalue *call = |
| gcc_jit_context_new_call (bfc->ctxt, |
| loc, |
| bfc->func_putchar, |
| 1, &arg); |
| gcc_jit_block_add_eval (bfc->curblock, |
| loc, |
| call); |
| } |
| |
| switch (ch) |
| { |
| case '>': |
| gcc_jit_block_add_comment (bfc->curblock, |
| loc, |
| "'>': idx += 1;"); |
| gcc_jit_block_add_assignment_op (bfc->curblock, |
| loc, |
| bfc->idx, |
| GCC_JIT_BINARY_OP_PLUS, |
| bfc->int_one); |
| break; |
| |
| case '<': |
| gcc_jit_block_add_comment (bfc->curblock, |
| loc, |
| "'<': idx -= 1;"); |
| gcc_jit_block_add_assignment_op (bfc->curblock, |
| loc, |
| bfc->idx, |
| GCC_JIT_BINARY_OP_MINUS, |
| bfc->int_one); |
| break; |
| |
| case '+': |
| gcc_jit_block_add_comment (bfc->curblock, |
| loc, |
| "'+': data[idx] += 1;"); |
| gcc_jit_block_add_assignment_op (bfc->curblock, |
| loc, |
| bf_get_current_data (bfc, loc), |
| GCC_JIT_BINARY_OP_PLUS, |
| bfc->byte_one); |
| break; |
| |
| case '-': |
| gcc_jit_block_add_comment (bfc->curblock, |
| loc, |
| "'-': data[idx] -= 1;"); |
| gcc_jit_block_add_assignment_op (bfc->curblock, |
| loc, |
| bf_get_current_data (bfc, loc), |
| GCC_JIT_BINARY_OP_MINUS, |
| bfc->byte_one); |
| break; |
| |
| case '.': |
| { |
| gcc_jit_rvalue *arg = |
| gcc_jit_context_new_cast ( |
| bfc->ctxt, |
| loc, |
| gcc_jit_lvalue_as_rvalue (bf_get_current_data (bfc, loc)), |
| bfc->int_type); |
| gcc_jit_rvalue *call = |
| gcc_jit_context_new_call (bfc->ctxt, |
| loc, |
| bfc->func_putchar, |
| 1, &arg); |
| gcc_jit_block_add_comment (bfc->curblock, |
| loc, |
| "'.': putchar ((int)data[idx]);"); |
| gcc_jit_block_add_eval (bfc->curblock, |
| loc, |
| call); |
| } |
| break; |
| |
| case ',': |
| { |
| gcc_jit_rvalue *call = |
| gcc_jit_context_new_call (bfc->ctxt, |
| loc, |
| bfc->func_getchar, |
| 0, NULL); |
| gcc_jit_block_add_comment ( |
| bfc->curblock, |
| loc, |
| "',': data[idx] = (unsigned char)getchar ();"); |
| gcc_jit_block_add_assignment (bfc->curblock, |
| loc, |
| bf_get_current_data (bfc, loc), |
| gcc_jit_context_new_cast ( |
| bfc->ctxt, |
| loc, |
| call, |
| bfc->byte_type)); |
| } |
| break; |
| |
| case '[': |
| { |
| gcc_jit_block *loop_test = |
| gcc_jit_function_new_block (bfc->func, NULL); |
| gcc_jit_block *on_zero = |
| gcc_jit_function_new_block (bfc->func, NULL); |
| gcc_jit_block *on_non_zero = |
| gcc_jit_function_new_block (bfc->func, NULL); |
| |
| if (bfc->num_open_parens == MAX_OPEN_PARENS) |
| fatal_error (bfc, "too many open parens"); |
| |
| gcc_jit_block_end_with_jump ( |
| bfc->curblock, |
| loc, |
| loop_test); |
| |
| gcc_jit_block_add_comment ( |
| loop_test, |
| loc, |
| "'['"); |
| gcc_jit_block_end_with_conditional ( |
| loop_test, |
| loc, |
| bf_current_data_is_zero (bfc, loc), |
| on_zero, |
| on_non_zero); |
| bfc->paren_test[bfc->num_open_parens] = loop_test; |
| bfc->paren_body[bfc->num_open_parens] = on_non_zero; |
| bfc->paren_after[bfc->num_open_parens] = on_zero; |
| bfc->num_open_parens += 1; |
| bfc->curblock = on_non_zero; |
| } |
| break; |
| |
| case ']': |
| { |
| gcc_jit_block_add_comment ( |
| bfc->curblock, |
| loc, |
| "']'"); |
| |
| if (bfc->num_open_parens == 0) |
| fatal_error (bfc, "mismatching parens"); |
| bfc->num_open_parens -= 1; |
| gcc_jit_block_end_with_jump ( |
| bfc->curblock, |
| loc, |
| bfc->paren_test[bfc->num_open_parens]); |
| bfc->curblock = bfc->paren_after[bfc->num_open_parens]; |
| } |
| break; |
| |
| case '\n': |
| bfc->line +=1; |
| bfc->column = 0; |
| break; |
| } |
| |
| if (ch != '\n') |
| bfc->column += 1; |
| } |
| |
| /* Compile the given .bf file into a gcc_jit_context, containing a |
| single "main" function suitable for compiling into an executable. */ |
| |
| gcc_jit_context * |
| bf_compile (const char *filename) |
| { |
| bf_compiler bfc; |
| FILE *f_in; |
| int ch; |
| |
| memset (&bfc, 0, sizeof (bfc)); |
| |
| bfc.filename = filename; |
| f_in = fopen (filename, "r"); |
| if (!f_in) |
| fatal_error (&bfc, "unable to open file"); |
| bfc.line = 1; |
| |
| bfc.ctxt = gcc_jit_context_acquire (); |
| |
| gcc_jit_context_set_int_option ( |
| bfc.ctxt, |
| GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, |
| 3); |
| gcc_jit_context_set_bool_option ( |
| bfc.ctxt, |
| GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, |
| 0); |
| gcc_jit_context_set_bool_option ( |
| bfc.ctxt, |
| GCC_JIT_BOOL_OPTION_DEBUGINFO, |
| 1); |
| gcc_jit_context_set_bool_option ( |
| bfc.ctxt, |
| GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING, |
| 0); |
| gcc_jit_context_set_bool_option ( |
| bfc.ctxt, |
| GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES, |
| 0); |
| |
| bfc.void_type = |
| gcc_jit_context_get_type (bfc.ctxt, GCC_JIT_TYPE_VOID); |
| bfc.int_type = |
| gcc_jit_context_get_type (bfc.ctxt, GCC_JIT_TYPE_INT); |
| bfc.byte_type = |
| gcc_jit_context_get_type (bfc.ctxt, GCC_JIT_TYPE_UNSIGNED_CHAR); |
| bfc.array_type = |
| gcc_jit_context_new_array_type (bfc.ctxt, |
| NULL, |
| bfc.byte_type, |
| 30000); |
| |
| bfc.func_getchar = |
| gcc_jit_context_new_function (bfc.ctxt, NULL, |
| GCC_JIT_FUNCTION_IMPORTED, |
| bfc.int_type, |
| "getchar", |
| 0, NULL, |
| 0); |
| |
| gcc_jit_param *param_c = |
| gcc_jit_context_new_param (bfc.ctxt, NULL, bfc.int_type, "c"); |
| bfc.func_putchar = |
| gcc_jit_context_new_function (bfc.ctxt, NULL, |
| GCC_JIT_FUNCTION_IMPORTED, |
| bfc.void_type, |
| "putchar", |
| 1, ¶m_c, |
| 0); |
| |
| bfc.func = make_main (bfc.ctxt); |
| bfc.curblock = |
| gcc_jit_function_new_block (bfc.func, "initial"); |
| bfc.int_zero = gcc_jit_context_zero (bfc.ctxt, bfc.int_type); |
| bfc.int_one = gcc_jit_context_one (bfc.ctxt, bfc.int_type); |
| bfc.byte_zero = gcc_jit_context_zero (bfc.ctxt, bfc.byte_type); |
| bfc.byte_one = gcc_jit_context_one (bfc.ctxt, bfc.byte_type); |
| |
| bfc.data_cells = |
| gcc_jit_context_new_global (bfc.ctxt, NULL, |
| GCC_JIT_GLOBAL_INTERNAL, |
| bfc.array_type, |
| "data_cells"); |
| bfc.idx = |
| gcc_jit_function_new_local (bfc.func, NULL, |
| bfc.int_type, |
| "idx"); |
| |
| gcc_jit_block_add_comment (bfc.curblock, |
| NULL, |
| "idx = 0;"); |
| gcc_jit_block_add_assignment (bfc.curblock, |
| NULL, |
| bfc.idx, |
| bfc.int_zero); |
| |
| bfc.num_open_parens = 0; |
| |
| while ( EOF != (ch = fgetc (f_in))) |
| bf_compile_char (&bfc, (unsigned char)ch); |
| |
| gcc_jit_block_end_with_return (bfc.curblock, NULL, bfc.int_zero); |
| |
| fclose (f_in); |
| |
| return bfc.ctxt; |
| } |
| |
| /* Entrypoint to the compiler. */ |
| |
| int |
| main (int argc, char **argv) |
| { |
| const char *input_file; |
| const char *output_file; |
| gcc_jit_context *ctxt; |
| const char *err; |
| |
| if (argc != 3) |
| { |
| fprintf (stderr, "%s: INPUT_FILE OUTPUT_FILE\n", argv[0]); |
| return 1; |
| } |
| |
| input_file = argv[1]; |
| output_file = argv[2]; |
| ctxt = bf_compile (input_file); |
| |
| gcc_jit_context_compile_to_file (ctxt, |
| GCC_JIT_OUTPUT_KIND_EXECUTABLE, |
| output_file); |
| |
| err = gcc_jit_context_get_first_error (ctxt); |
| |
| if (err) |
| { |
| gcc_jit_context_release (ctxt); |
| return 1; |
| } |
| |
| gcc_jit_context_release (ctxt); |
| return 0; |
| } |
| |
| /* Use the built compiler to compile the example to an executable: |
| |
| { dg-jit-set-exe-params SRCDIR/gcc/jit/docs/examples/emit-alphabet.bf emit-alphabet.bf.exe } |
| |
| Then run the executable, and verify that it emits the alphabet: |
| |
| { dg-final { jit-run-executable emit-alphabet.bf.exe "ABCDEFGHIJKLMNOPQRSTUVWXYZ" } } */ |