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 *
make_main(gcc_jit_context * ctxt)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
fatal_error(bf_compiler * bfc,const char * msg)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 *
bf_get_current_data(bf_compiler * bfc,gcc_jit_location * loc)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 *
bf_current_data_is_zero(bf_compiler * bfc,gcc_jit_location * loc)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
bf_compile_char(bf_compiler * bfc,unsigned char ch)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 *
bf_compile(const char * filename)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, &param_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
main(int argc,char ** argv)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" } } */
447