Implementing an Ahead-of-Time compiler — libgccjit v5.1.0 ( ) documentation (original) (raw)

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, &param_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; }