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, ¶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
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