1  /* A compiler for the "bf" language.  */
       2  
       3  #include <stdlib.h>
       4  #include <string.h>
       5  #include <errno.h>
       6  
       7  #include "libgccjit.h"
       8  
       9  /* Make "main" function:
      10       int
      11       main (int argc, char **argv)
      12       {
      13         ...
      14       }
      15  */
      16  static gcc_jit_function *
      17  make_main (gcc_jit_context *ctxt)
      18  {
      19    gcc_jit_type *int_type =
      20      gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
      21    gcc_jit_param *param_argc =
      22      gcc_jit_context_new_param (ctxt, NULL, int_type, "argc");
      23    gcc_jit_type *char_ptr_ptr_type =
      24      gcc_jit_type_get_pointer (
      25        gcc_jit_type_get_pointer (
      26  	gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_CHAR)));
      27    gcc_jit_param *param_argv =
      28      gcc_jit_context_new_param (ctxt, NULL, char_ptr_ptr_type, "argv");
      29    gcc_jit_param *params[2] = {param_argc, param_argv};
      30    gcc_jit_function *func_main =
      31      gcc_jit_context_new_function (ctxt, NULL,
      32  				  GCC_JIT_FUNCTION_EXPORTED,
      33  				  int_type,
      34  				  "main",
      35  				  2, params,
      36  				  0);
      37    return func_main;
      38  }
      39  
      40  #define MAX_OPEN_PARENS 16
      41  
      42  typedef struct bf_compiler
      43  {
      44    const char *filename;
      45    int line;
      46    int column;
      47  
      48    gcc_jit_context *ctxt;
      49  
      50    gcc_jit_type *void_type;
      51    gcc_jit_type *int_type;
      52    gcc_jit_type *byte_type;
      53    gcc_jit_type *array_type;
      54  
      55    gcc_jit_function *func_getchar;
      56    gcc_jit_function *func_putchar;
      57  
      58    gcc_jit_function *func;
      59    gcc_jit_block *curblock;
      60  
      61    gcc_jit_rvalue *int_zero;
      62    gcc_jit_rvalue *int_one;
      63    gcc_jit_rvalue *byte_zero;
      64    gcc_jit_rvalue *byte_one;
      65    gcc_jit_lvalue *data_cells;
      66    gcc_jit_lvalue *idx;
      67  
      68    int num_open_parens;
      69    gcc_jit_block *paren_test[MAX_OPEN_PARENS];
      70    gcc_jit_block *paren_body[MAX_OPEN_PARENS];
      71    gcc_jit_block *paren_after[MAX_OPEN_PARENS];
      72  
      73  } bf_compiler;
      74  
      75  /* Bail out, with a message on stderr.  */
      76  
      77  static void
      78  fatal_error (bf_compiler *bfc, const char *msg)
      79  {
      80    fprintf (stderr,
      81  	   "%s:%i:%i: %s",
      82  	   bfc->filename, bfc->line, bfc->column, msg);
      83    abort ();
      84  }
      85  
      86  /* Get "data_cells[idx]" as an lvalue.  */
      87  
      88  static gcc_jit_lvalue *
      89  bf_get_current_data (bf_compiler *bfc, gcc_jit_location *loc)
      90  {
      91    return gcc_jit_context_new_array_access (
      92      bfc->ctxt,
      93      loc,
      94      gcc_jit_lvalue_as_rvalue (bfc->data_cells),
      95      gcc_jit_lvalue_as_rvalue (bfc->idx));
      96  }
      97  
      98  /* Get "data_cells[idx] == 0" as a boolean rvalue.  */
      99  
     100  static gcc_jit_rvalue *
     101  bf_current_data_is_zero (bf_compiler *bfc, gcc_jit_location *loc)
     102  {
     103    return gcc_jit_context_new_comparison (
     104      bfc->ctxt,
     105      loc,
     106      GCC_JIT_COMPARISON_EQ,
     107      gcc_jit_lvalue_as_rvalue (bf_get_current_data (bfc, loc)),
     108      bfc->byte_zero);
     109  }
     110  
     111  /* Compile one bf character.  */
     112  
     113  static void
     114  bf_compile_char (bf_compiler *bfc,
     115  		 unsigned char ch)
     116  {
     117    gcc_jit_location *loc =
     118      gcc_jit_context_new_location (bfc->ctxt,
     119  				  bfc->filename,
     120  				  bfc->line,
     121  				  bfc->column);
     122  
     123    /* Turn this on to trace execution, by injecting putchar ()
     124       of each source char. */
     125    if (0)
     126      {
     127        gcc_jit_rvalue *arg =
     128  	gcc_jit_context_new_rvalue_from_int (
     129  					     bfc->ctxt,
     130  					     bfc->int_type,
     131  					     ch);
     132        gcc_jit_rvalue *call =
     133  	gcc_jit_context_new_call (bfc->ctxt,
     134  				  loc,
     135  				  bfc->func_putchar,
     136  				  1, &arg);
     137        gcc_jit_block_add_eval (bfc->curblock,
     138  			      loc,
     139  			      call);
     140      }
     141  
     142    switch (ch)
     143      {
     144        case '>':
     145  	gcc_jit_block_add_comment (bfc->curblock,
     146  				   loc,
     147  				   "'>': idx += 1;");
     148  	gcc_jit_block_add_assignment_op (bfc->curblock,
     149  					 loc,
     150  					 bfc->idx,
     151  					 GCC_JIT_BINARY_OP_PLUS,
     152  					 bfc->int_one);
     153  	break;
     154  
     155        case '<':
     156  	gcc_jit_block_add_comment (bfc->curblock,
     157  				   loc,
     158  				   "'<': idx -= 1;");
     159  	gcc_jit_block_add_assignment_op (bfc->curblock,
     160  					 loc,
     161  					 bfc->idx,
     162  					 GCC_JIT_BINARY_OP_MINUS,
     163  					 bfc->int_one);
     164  	break;
     165  
     166        case '+':
     167  	gcc_jit_block_add_comment (bfc->curblock,
     168  				   loc,
     169  				   "'+': data[idx] += 1;");
     170  	gcc_jit_block_add_assignment_op (bfc->curblock,
     171  					 loc,
     172  					 bf_get_current_data (bfc, loc),
     173  					 GCC_JIT_BINARY_OP_PLUS,
     174  					 bfc->byte_one);
     175  	break;
     176  
     177        case '-':
     178  	gcc_jit_block_add_comment (bfc->curblock,
     179  				   loc,
     180  				   "'-': data[idx] -= 1;");
     181  	gcc_jit_block_add_assignment_op (bfc->curblock,
     182  					 loc,
     183  					 bf_get_current_data (bfc, loc),
     184  					 GCC_JIT_BINARY_OP_MINUS,
     185  					 bfc->byte_one);
     186  	break;
     187  
     188        case '.':
     189  	{
     190  	  gcc_jit_rvalue *arg =
     191  	    gcc_jit_context_new_cast (
     192  	      bfc->ctxt,
     193  	      loc,
     194  	      gcc_jit_lvalue_as_rvalue (bf_get_current_data (bfc, loc)),
     195  	      bfc->int_type);
     196  	  gcc_jit_rvalue *call =
     197  	    gcc_jit_context_new_call (bfc->ctxt,
     198  				      loc,
     199  				      bfc->func_putchar,
     200  				      1, &arg);
     201  	  gcc_jit_block_add_comment (bfc->curblock,
     202  				     loc,
     203  				     "'.': putchar ((int)data[idx]);");
     204  	  gcc_jit_block_add_eval (bfc->curblock,
     205  				  loc,
     206  				  call);
     207  	}
     208  	break;
     209  
     210        case ',':
     211  	{
     212  	  gcc_jit_rvalue *call =
     213  	    gcc_jit_context_new_call (bfc->ctxt,
     214  				      loc,
     215  				      bfc->func_getchar,
     216  				      0, NULL);
     217  	  gcc_jit_block_add_comment (
     218  	    bfc->curblock,
     219  	    loc,
     220  	    "',': data[idx] = (unsigned char)getchar ();");
     221  	  gcc_jit_block_add_assignment (bfc->curblock,
     222  					loc,
     223  					bf_get_current_data (bfc, loc),
     224  					gcc_jit_context_new_cast (
     225  					  bfc->ctxt,
     226  					  loc,
     227  					  call,
     228  					  bfc->byte_type));
     229  	}
     230  	break;
     231  
     232        case '[':
     233  	{
     234  	  gcc_jit_block *loop_test =
     235  	    gcc_jit_function_new_block (bfc->func, NULL);
     236  	  gcc_jit_block *on_zero =
     237  	    gcc_jit_function_new_block (bfc->func, NULL);
     238  	  gcc_jit_block *on_non_zero =
     239  	    gcc_jit_function_new_block (bfc->func, NULL);
     240  
     241  	  if (bfc->num_open_parens == MAX_OPEN_PARENS)
     242  	    fatal_error (bfc, "too many open parens");
     243  
     244  	  gcc_jit_block_end_with_jump (
     245  	    bfc->curblock,
     246  	    loc,
     247  	    loop_test);
     248  
     249  	  gcc_jit_block_add_comment (
     250  	    loop_test,
     251  	    loc,
     252  	    "'['");
     253  	  gcc_jit_block_end_with_conditional (
     254  	    loop_test,
     255  	    loc,
     256  	    bf_current_data_is_zero (bfc, loc),
     257  	    on_zero,
     258  	    on_non_zero);
     259  	  bfc->paren_test[bfc->num_open_parens] = loop_test;
     260  	  bfc->paren_body[bfc->num_open_parens] = on_non_zero;
     261  	  bfc->paren_after[bfc->num_open_parens] = on_zero;
     262  	  bfc->num_open_parens += 1;
     263  	  bfc->curblock = on_non_zero;
     264  	}
     265  	break;
     266  
     267        case ']':
     268  	{
     269  	  gcc_jit_block_add_comment (
     270  	    bfc->curblock,
     271  	    loc,
     272  	    "']'");
     273  
     274  	  if (bfc->num_open_parens == 0)
     275  	    fatal_error (bfc, "mismatching parens");
     276  	  bfc->num_open_parens -= 1;
     277  	  gcc_jit_block_end_with_jump (
     278  	    bfc->curblock,
     279  	    loc,
     280  	    bfc->paren_test[bfc->num_open_parens]);
     281  	  bfc->curblock = bfc->paren_after[bfc->num_open_parens];
     282  	}
     283  	break;
     284  
     285      case '\n':
     286        bfc->line +=1;
     287        bfc->column = 0;
     288        break;
     289      }
     290  
     291    if (ch != '\n')
     292      bfc->column += 1;
     293  }
     294  
     295  /* Compile the given .bf file into a gcc_jit_context, containing a
     296     single "main" function suitable for compiling into an executable.  */
     297  
     298  gcc_jit_context *
     299  bf_compile (const char *filename)
     300  {
     301    bf_compiler bfc;
     302    FILE *f_in;
     303    int ch;
     304  
     305    memset (&bfc, 0, sizeof (bfc));
     306  
     307    bfc.filename = filename;
     308    f_in = fopen (filename, "r");
     309    if (!f_in)
     310      fatal_error (&bfc, "unable to open file");
     311    bfc.line = 1;
     312  
     313    bfc.ctxt = gcc_jit_context_acquire ();
     314  
     315    gcc_jit_context_set_int_option (
     316      bfc.ctxt,
     317      GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
     318      3);
     319    gcc_jit_context_set_bool_option (
     320      bfc.ctxt,
     321      GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE,
     322      0);
     323    gcc_jit_context_set_bool_option (
     324      bfc.ctxt,
     325      GCC_JIT_BOOL_OPTION_DEBUGINFO,
     326      1);
     327    gcc_jit_context_set_bool_option (
     328      bfc.ctxt,
     329      GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING,
     330      0);
     331    gcc_jit_context_set_bool_option (
     332      bfc.ctxt,
     333      GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES,
     334      0);
     335  
     336    bfc.void_type =
     337      gcc_jit_context_get_type (bfc.ctxt, GCC_JIT_TYPE_VOID);
     338    bfc.int_type =
     339      gcc_jit_context_get_type (bfc.ctxt, GCC_JIT_TYPE_INT);
     340    bfc.byte_type =
     341      gcc_jit_context_get_type (bfc.ctxt, GCC_JIT_TYPE_UNSIGNED_CHAR);
     342    bfc.array_type =
     343      gcc_jit_context_new_array_type (bfc.ctxt,
     344  				    NULL,
     345  				    bfc.byte_type,
     346  				    30000);
     347  
     348    bfc.func_getchar =
     349      gcc_jit_context_new_function (bfc.ctxt, NULL,
     350  				  GCC_JIT_FUNCTION_IMPORTED,
     351  				  bfc.int_type,
     352  				  "getchar",
     353  				  0, NULL,
     354  				  0);
     355  
     356    gcc_jit_param *param_c =
     357      gcc_jit_context_new_param (bfc.ctxt, NULL, bfc.int_type, "c");
     358    bfc.func_putchar =
     359      gcc_jit_context_new_function (bfc.ctxt, NULL,
     360  				  GCC_JIT_FUNCTION_IMPORTED,
     361  				  bfc.void_type,
     362  				  "putchar",
     363  				  1, ¶m_c,
     364  				  0);
     365  
     366    bfc.func = make_main (bfc.ctxt);
     367     bfc.curblock =
     368      gcc_jit_function_new_block (bfc.func, "initial");
     369    bfc.int_zero = gcc_jit_context_zero (bfc.ctxt, bfc.int_type);
     370    bfc.int_one = gcc_jit_context_one (bfc.ctxt, bfc.int_type);
     371    bfc.byte_zero = gcc_jit_context_zero (bfc.ctxt, bfc.byte_type);
     372    bfc.byte_one = gcc_jit_context_one (bfc.ctxt, bfc.byte_type);
     373  
     374    bfc.data_cells =
     375      gcc_jit_context_new_global (bfc.ctxt, NULL,
     376  				 GCC_JIT_GLOBAL_INTERNAL,
     377  				 bfc.array_type,
     378  				 "data_cells");
     379    bfc.idx =
     380      gcc_jit_function_new_local (bfc.func, NULL,
     381  				bfc.int_type,
     382  				"idx");
     383  
     384    gcc_jit_block_add_comment (bfc.curblock,
     385  			     NULL,
     386  			     "idx = 0;");
     387    gcc_jit_block_add_assignment (bfc.curblock,
     388  				NULL,
     389  				bfc.idx,
     390  				bfc.int_zero);
     391  
     392    bfc.num_open_parens = 0;
     393  
     394    while ( EOF != (ch = fgetc (f_in)))
     395      bf_compile_char (&bfc, (unsigned char)ch);
     396  
     397    gcc_jit_block_end_with_return (bfc.curblock, NULL, bfc.int_zero);
     398  
     399    fclose (f_in);
     400  
     401    return bfc.ctxt;
     402  }
     403  
     404  /* Entrypoint to the compiler.  */
     405  
     406  int
     407  main (int argc, char **argv)
     408  {
     409    const char *input_file;
     410    const char *output_file;
     411    gcc_jit_context *ctxt;
     412    const char *err;
     413  
     414    if (argc != 3)
     415      {
     416        fprintf (stderr, "%s: INPUT_FILE OUTPUT_FILE\n", argv[0]);
     417        return 1;
     418      }
     419  
     420    input_file = argv[1];
     421    output_file = argv[2];
     422    ctxt = bf_compile (input_file);
     423  
     424    gcc_jit_context_compile_to_file (ctxt,
     425  				   GCC_JIT_OUTPUT_KIND_EXECUTABLE,
     426  				   output_file);
     427  
     428    err = gcc_jit_context_get_first_error (ctxt);
     429  
     430    if (err)
     431      {
     432        gcc_jit_context_release (ctxt);
     433        return 1;
     434      }
     435  
     436    gcc_jit_context_release (ctxt);
     437    return 0;
     438  }
     439  
     440  /* Use the built compiler to compile the example to an executable:
     441  
     442       { dg-jit-set-exe-params SRCDIR/gcc/jit/docs/examples/emit-alphabet.bf emit-alphabet.bf.exe }
     443  
     444     Then run the executable, and verify that it emits the alphabet:
     445  
     446       { dg-final { jit-run-executable emit-alphabet.bf.exe "ABCDEFGHIJKLMNOPQRSTUVWXYZ" } } */