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" } } */ 447