Add options:
[cparser] / main.c
1 #include <config.h>
2
3 #define _GNU_SOURCE
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdbool.h>
8 #include <errno.h>
9 #include <string.h>
10 #include <assert.h>
11
12 #ifdef _WIN32
13
14 #include <fcntl.h>
15 #include <io.h>
16
17 /* no eXecute on Win32 */
18 #define X_OK 0
19 #define W_OK 2
20 #define R_OK 4
21
22 #define O_RDWR          _O_RDWR
23 #define O_CREAT         _O_CREAT
24 #define O_EXCL          _O_EXCL
25 #define O_BINARY        _O_BINARY
26
27 /* remap some names, we are not in the POSIX world */
28 #define access(fname, mode)      _access(fname, mode)
29 #define mktemp(tmpl)             _mktemp(tmpl)
30 #define open(fname, oflag, mode) _open(fname, oflag, mode)
31 #define fdopen(fd, mode)         _fdopen(fd, mode)
32 #define popen(cmd, mode)         _popen(cmd, mode)
33 #define pclose(file)             _pclose(file)
34
35 #else
36 #include <unistd.h>
37 #define HAVE_MKSTEMP
38 #endif
39
40 #ifndef WITH_LIBCORE
41 #define WITH_LIBCORE
42 #endif
43
44 #include <libfirm/firm.h>
45 #include <libfirm/be.h>
46
47 #include "lexer.h"
48 #include "token_t.h"
49 #include "type_hash.h"
50 #include "parser.h"
51 #include "ast2firm.h"
52 #include "lang_features.h"
53 #include "driver/firm_cmdline.h"
54 #include "adt/error.h"
55 #include "write_fluffy.h"
56 #include "driver/firm_opt.h"
57
58 #ifndef PREPROCESSOR
59 #define PREPROCESSOR "cpp -std=c99 -U__WCHAR_TYPE__ -D__WCHAR_TYPE__=int"
60 #endif
61
62 #ifndef LINKER
63 #define LINKER    "gcc -m32"
64 #endif
65
66 #ifndef ASSEMBLER
67 #define ASSEMBLER "as --32"
68 #endif
69
70 /** The current c mode/dialect. */
71 unsigned int c_mode = _C99|_GNUC;
72
73 /** The 'machine size', 16, 32 or 64 bit, 32bit is the default. */
74 unsigned int machine_size = 32;
75
76 /** true if the char type is signed. */
77 bool char_is_signed = true;
78
79 static int            verbose;
80 static struct obstack cppflags_obst;
81
82 #if defined(_DEBUG) || defined(FIRM_DEBUG)
83 /**
84  * Debug printf implementation.
85  *
86  * @param fmt  printf style format parameter
87  */
88 void dbg_printf(const char *fmt, ...)
89 {
90         va_list list;
91
92         if (firm_dump.debug_print) {
93                 va_start(list, fmt);
94                 vprintf(fmt, list);
95                 va_end(list);
96         }  /* if */
97 }
98 #endif /* defined(_DEBUG) || defined(FIRM_DEBUG) */
99
100 static void initialize_firm(void)
101 {
102         firm_early_init();
103
104         dump_consts_local(1);
105         dump_keepalive_edges(1);
106 }
107
108 static void get_output_name(char *buf, size_t buflen, const char *inputname,
109                             const char *newext)
110 {
111         size_t last_dot = 0xffffffff;
112         size_t i = 0;
113
114         if(inputname == NULL) {
115                 snprintf(buf, buflen, "a%s", newext);
116                 return;
117         }
118
119         for(const char *c = inputname; *c != 0; ++c) {
120                 if(*c == '.')
121                         last_dot = i;
122                 ++i;
123         }
124         if(last_dot == 0xffffffff)
125                 last_dot = i;
126
127         if(last_dot >= buflen)
128                 panic("filename too long");
129         memcpy(buf, inputname, last_dot);
130
131         size_t extlen = strlen(newext) + 1;
132         if(extlen + last_dot >= buflen)
133                 panic("filename too long");
134         memcpy(buf+last_dot, newext, extlen);
135 }
136
137 static translation_unit_t *do_parsing(FILE *const in, const char *const input)
138 {
139         lexer_open_stream(in, input);
140         translation_unit_t *unit = parse();
141         return unit;
142 }
143
144 static void lextest(FILE *in, const char *fname)
145 {
146         lexer_open_stream(in, fname);
147
148         do {
149                 lexer_next_preprocessing_token();
150                 print_token(stdout, &lexer_token);
151                 puts("");
152         } while(lexer_token.type != T_EOF);
153 }
154
155 static FILE* preprocess(FILE* in, const char *fname)
156 {
157         char buf[4096];
158         const char *flags = obstack_finish(&cppflags_obst);
159
160         if(in != stdin) {
161                 snprintf(buf, sizeof(buf), PREPROCESSOR " %s %s", flags, fname);
162         } else {
163                 /* read from stdin */
164                 snprintf(buf, sizeof(buf), PREPROCESSOR " %s -", flags);
165         }
166
167         if(verbose) {
168                 puts(buf);
169         }
170         FILE* f = popen(buf, "r");
171         if (f == NULL) {
172                 fprintf(stderr, "invoking preprocessor failed\n");
173                 exit(1);
174         }
175         return f;
176 }
177
178 static void do_link(const char *out, const char *in)
179 {
180         char buf[4096];
181
182         snprintf(buf, sizeof(buf), "%s %s -o %s", LINKER, in, out);
183         if(verbose) {
184                 puts(buf);
185         }
186         int err = system(buf);
187         if(err != 0) {
188                 fprintf(stderr, "linker reported an error\n");
189                 exit(1);
190         }
191 }
192
193 static void assemble(const char *out, const char *in)
194 {
195         char buf[4096];
196
197         snprintf(buf, sizeof(buf), "%s %s -o %s", ASSEMBLER, in, out);
198         if(verbose) {
199                 puts(buf);
200         }
201
202         int err = system(buf);
203         if(err != 0) {
204                 fprintf(stderr, "assembler reported an error\n");
205                 exit(1);
206         }
207 }
208
209 static const char *try_dir(const char *dir)
210 {
211         if(dir == NULL)
212                 return dir;
213         if(access(dir, R_OK | W_OK | X_OK) == 0)
214                 return dir;
215         return NULL;
216 }
217
218 static const char *get_tempdir(void)
219 {
220         static const char *tmpdir = NULL;
221
222         if(tmpdir != NULL)
223                 return tmpdir;
224
225         if(tmpdir == NULL)
226                 tmpdir = try_dir(getenv("TMPDIR"));
227         if(tmpdir == NULL)
228                 tmpdir = try_dir(getenv("TMP"));
229         if(tmpdir == NULL)
230                 tmpdir = try_dir(getenv("TEMP"));
231
232 #ifdef P_tmpdir
233         if(tmpdir == NULL)
234                 tmpdir = try_dir(P_tmpdir);
235 #endif
236
237         if(tmpdir == NULL)
238                 tmpdir = try_dir("/var/tmp");
239         if(tmpdir == NULL)
240                 tmpdir = try_dir("/usr/tmp");
241         if(tmpdir == NULL)
242                 tmpdir = try_dir("/tmp");
243
244         if(tmpdir == NULL)
245                 tmpdir = ".";
246
247         return tmpdir;
248 }
249
250 #ifndef HAVE_MKSTEMP
251 /* cheap and nasty mkstemp replacement */
252 static int mkstemp(char *templ)
253 {
254         mktemp(templ);
255         return open(templ, O_RDWR|O_CREAT|O_EXCL|O_BINARY, 0600);
256 }
257 #endif
258
259 /**
260  * an own version of tmpnam, which: writes in a buffer, appends a user specified
261  * suffix, emits no warnings during linking (like glibc/gnu ld do for tmpnam)...
262  */
263 static FILE *make_temp_file(char *buffer, size_t buflen,
264                             const char *prefix, const char *suffix)
265 {
266         const char *tempdir = get_tempdir();
267
268         /* oh well... mkstemp doesn't accept a suffix after XXXXXX... */
269         (void) suffix;
270         suffix = "";
271
272         snprintf(buffer, buflen, "%s/%sXXXXXX%s", tempdir, prefix, suffix);
273
274         int fd = mkstemp(buffer);
275         if(fd == -1) {
276                 fprintf(stderr, "couldn't create temporary file: %s\n",
277                         strerror(errno));
278                 exit(1);
279         }
280         FILE *out = fdopen(fd, "w");
281         if(out == NULL) {
282                 fprintf(stderr, "couldn't create temporary file FILE*\n");
283                 exit(1);
284         }
285
286         return out;
287 }
288
289 /**
290  * Do the necessary lowering for compound parameters.
291  */
292 void lower_compound_params(void)
293 {
294         lower_params_t params;
295
296         params.def_ptr_alignment    = 4;
297         params.flags                = LF_COMPOUND_RETURN | LF_RETURN_HIDDEN;
298         params.hidden_params        = ADD_HIDDEN_ALWAYS_IN_FRONT;
299         params.find_pointer_type    = NULL;
300         params.ret_compound_in_regs = NULL;
301         lower_calls_with_compounds(&params);
302 }
303
304 typedef enum compile_mode_t {
305         ParseOnly,
306         Compile,
307         CompileDump,
308         CompileAssemble,
309         CompileAssembleLink,
310         LexTest,
311         PrintAst,
312         PrintFluffy
313 } compile_mode_t;
314
315 static void usage(const char *argv0)
316 {
317         fprintf(stderr, "Usage %s input [-o output] [-c]\n", argv0);
318 }
319
320 int main(int argc, char **argv)
321 {
322         initialize_firm();
323
324         const char     *input        = NULL;
325         const char     *outname      = NULL;
326         const char     *dumpfunction = NULL;
327         compile_mode_t  mode         = CompileAssembleLink;
328
329         obstack_init(&cppflags_obst);
330
331 #define GET_ARG_AFTER(def, args)                                             \
332         def = &arg[sizeof(args)-1];                                              \
333         if(def[0] == '\0') {                                                     \
334                 ++i;                                                                 \
335                 if(i >= argc) {                                                      \
336                         fprintf(stderr, "error: expected argument after '" args "'\n");  \
337                         argument_errors = true;                                          \
338                         break;                                                           \
339                 }                                                                    \
340                 def = argv[i];                                                       \
341                 if(def[0] == '-' && def[1] != '\0') {                                \
342                         fprintf(stderr, "error: expected argument after '" args "'\n");  \
343                         argument_errors = true;                                          \
344                         continue;                                                        \
345                 }                                                                    \
346         }
347
348         bool help_displayed  = false;
349         bool argument_errors = false;
350         for(int i = 1; i < argc; ++i) {
351                 const char *arg = argv[i];
352                 if(strncmp(arg, "-o", 2) == 0) {
353                         GET_ARG_AFTER(outname, "-o");
354                 } else if(strcmp(arg, "-c") == 0) {
355                         mode = CompileAssemble;
356                 } else if(strcmp(arg, "-S") == 0) {
357                         mode = Compile;
358                 } else if(strcmp(arg, "--gcc") == 0) {
359                         c_mode |= _GNUC;
360                 } else if(strcmp(arg, "--no-gcc") == 0) {
361                         c_mode &= ~_GNUC;
362                 } else if(strcmp(arg, "--ms") == 0) {
363                         c_mode |= _MS;
364                 } else if(strcmp(arg, "--signed-chars") == 0) {
365                         char_is_signed = true;
366                 } else if(strcmp(arg, "--unsigned-chars") == 0) {
367                         char_is_signed = false;
368                 } else if(strcmp(arg, "--no-ms") == 0) {
369                         c_mode &= ~_MS;
370                 } else if(strcmp(arg, "--lextest") == 0) {
371                         mode = LexTest;
372                 } else if(strcmp(arg, "--print-ast") == 0) {
373                         mode = PrintAst;
374                 } else if(strcmp(arg, "--print-fluffy") == 0) {
375                         mode = PrintFluffy;
376                 } else if(strcmp(arg, "-fsyntax-only") == 0) {
377                         mode = ParseOnly;
378                 } else if(strncmp(arg, "-I", 2) == 0) {
379                         const char *opt;
380                         GET_ARG_AFTER(opt, "-I");
381                         obstack_printf(&cppflags_obst, " -I%s", opt);
382                 } else if(strncmp(arg, "-D", 2) == 0) {
383                         const char *opt;
384                         GET_ARG_AFTER(opt, "-D");
385                         obstack_printf(&cppflags_obst, " -D%s", opt);
386                 } else if(strcmp(arg, "--dump-function") == 0) {
387                         ++i;
388                         if(i >= argc) {
389                                 fprintf(stderr, "error: "
390                                         "expected argument after '--dump-function'\n");
391                                 argument_errors = true;
392                                 break;
393                         }
394                         dumpfunction = argv[i];
395                         mode         = CompileDump;
396                 } else if(strcmp(arg, "-v") == 0) {
397                         verbose = 1;
398                 } else if(arg[0] == '-' && arg[1] == 'f') {
399                         const char *opt;
400                         GET_ARG_AFTER(opt, "-f");
401                         int res = firm_option(opt);
402                         if (res == 0) {
403                                 fprintf(stderr, "error: unknown Firm option '-f %s'\n", opt);
404                                 argument_errors = true;
405                                 continue;
406                         } else if (res == -1) {
407                                 help_displayed = true;
408                         }
409                 } else if(arg[0] == '-' && arg[1] == 'b') {
410                         const char *opt;
411                         GET_ARG_AFTER(opt, "-b");
412                         int res = firm_be_option(opt);
413                         if (res == 0) {
414                                 fprintf(stderr, "error: unknown Firm backend option '-b %s'\n",
415                                         opt);
416                                 argument_errors = true;
417                         } else if (res == -1) {
418                                 help_displayed = true;
419                         }
420                 } else if(arg[0] == '-' && arg[1] == 'm') {
421                         const char *opt;
422                         GET_ARG_AFTER(opt, "-m");
423                         char *endptr;
424                         long int value = strtol(opt, &endptr, 10);
425                         if (*endptr != '\0') {
426                                 fprintf(stderr, "error: wrong option '-m %s'\n",  opt);
427                                 argument_errors = true;
428                         }
429                         if (value != 16 && value != 32 && value != 64) {
430                                 fprintf(stderr, "error: option -m supports only 16, 32 or 64\n");
431                                 argument_errors = true;
432                         } else
433                                 machine_size = (unsigned int)value;
434                 } else if(arg[0] == '-') {
435                         if (arg[1] == '\0') {
436                                 if(input != NULL) {
437                                         fprintf(stderr, "error: multiple input files specified\n");
438                                         argument_errors = true;
439                                 } else {
440                                         input = arg;
441                                 }
442                         } else if(strcmp(arg, "-pedantic") == 0) {
443                                 fprintf(stderr, "warning: ignoring gcc option '%s'\n", arg);
444                         } else if(arg[1] == 'O' ||
445                                         arg[1] == 'f' ||
446                                         arg[1] == 'W' ||
447                                         arg[1] == 'g' ||
448                                         strncmp(arg + 1, "std=", 4) == 0) {
449                                 fprintf(stderr, "warning: ignoring gcc option '%s'\n", arg);
450                         } else {
451                                 fprintf(stderr, "error: unknown argument '%s'\n", arg);
452                                 argument_errors = true;
453                         }
454                 } else {
455                         if(input != NULL) {
456                                 fprintf(stderr, "error: multiple input files specified\n");
457                                 argument_errors = true;
458                         } else {
459                                 input = arg;
460                         }
461                 }
462         }
463
464         if(help_displayed) {
465                 return !argument_errors;
466         }
467         if(argument_errors) {
468                 usage(argv[0]);
469                 return 1;
470         }
471
472         gen_firm_init();
473         init_symbol_table();
474         init_tokens();
475         init_types();
476         init_typehash();
477         init_lexer();
478         init_ast();
479         init_parser();
480         init_ast2firm();
481
482         FILE *out;
483         char  outnamebuf[4096];
484         if(outname == NULL) {
485                 switch(mode) {
486                 case PrintAst:
487                 case PrintFluffy:
488                 case LexTest:
489                         if(outname == NULL)
490                                 outname = "-";
491                         break;
492                 case ParseOnly:
493                         break;
494                 case Compile:
495                         get_output_name(outnamebuf, sizeof(outnamebuf), input, ".s");
496                         outname = outnamebuf;
497                         break;
498                 case CompileAssemble:
499                         get_output_name(outnamebuf, sizeof(outnamebuf), input, ".o");
500                         outname = outnamebuf;
501                         break;
502                 case CompileDump:
503                         get_output_name(outnamebuf, sizeof(outnamebuf), dumpfunction,
504                                         ".vcg");
505                         outname = outnamebuf;
506                         break;
507                 case CompileAssembleLink:
508                         outname = "a.out";
509                         break;
510                 }
511         }
512
513         if(outname != NULL) {
514                 if(strcmp(outname, "-") == 0) {
515                         out = stdout;
516                 } else {
517                         out = fopen(outname, "w");
518                         if(out == NULL) {
519                                 fprintf(stderr, "Couldn't open '%s' for writing: %s\n", outname,
520                                         strerror(errno));
521                                 return 1;
522                         }
523                 }
524         }
525
526         FILE *in;
527         if(input == NULL) {
528                 fprintf(stderr, "%s: no input files\n", argv[0]);
529                 return 1;
530         } else if(strcmp(input, "-") == 0) {
531                 in    = stdin;
532                 input = "<stdin>";
533         } else {
534                 in = fopen(input, "r");
535                 if(in == NULL) {
536                         fprintf(stderr, "Couldn't open '%s': %s\n", input, strerror(errno));
537                         return 1;
538                 }
539         }
540
541         if(mode == LexTest) {
542                 lextest(in, input);
543                 fclose(in);
544                 return 0;
545         }
546
547         FILE *preprocessed_in = preprocess(in, input);
548         translation_unit_t *const unit = do_parsing(preprocessed_in, input);
549         pclose(preprocessed_in);
550         if(unit == NULL)
551                 return 1;
552
553         if(mode == PrintAst) {
554                 type_set_output(out);
555                 ast_set_output(out);
556                 print_ast(unit);
557                 return 0;
558         }
559         if(mode == PrintFluffy) {
560                 type_set_output(out);
561                 ast_set_output(out);
562                 write_fluffy_decls(out, unit);
563         }
564
565         translation_unit_to_firm(unit);
566
567         if(mode == ParseOnly) {
568                 return 0;
569         }
570
571         FILE *asm_out;
572         char  asm_tempfile[1024];
573         if(mode == CompileDump) {
574                 asm_out = NULL;
575                 firm_be_opt.selection = BE_NONE;
576         } else if(mode == Compile) {
577                 asm_out = out;
578         } else {
579                 asm_out
580                         = make_temp_file(asm_tempfile, sizeof(asm_tempfile), "cc", ".s");
581         }
582         gen_firm_finish(asm_out, input, /*c_mode=*/1, /*firm_const_exists=*/0);
583
584         if(mode == CompileDump) {
585                 /* find irg */
586                 ident    *id     = new_id_from_str(dumpfunction);
587                 ir_graph *irg    = NULL;
588                 int       n_irgs = get_irp_n_irgs();
589                 for(int i = 0; i < n_irgs; ++i) {
590                         ir_graph *tirg   = get_irp_irg(i);
591                         ident    *irg_id = get_entity_ident(get_irg_entity(tirg));
592                         if(irg_id == id) {
593                                 irg = tirg;
594                                 break;
595                         }
596                 }
597
598                 if(irg == NULL) {
599                         fprintf(stderr, "No graph for function '%s' found\n", dumpfunction);
600                         return 1;
601                 }
602
603                 dump_ir_block_graph_file(irg, out);
604                 fclose(out);
605                 return 0;
606         }
607
608         fclose(asm_out);
609
610         /* assemble assembler and create object file */
611         char obj_tfile[1024];
612         if(mode == CompileAssemble || mode == CompileAssembleLink) {
613                 const char *obj_outfile;
614                 if(mode == CompileAssemble) {
615                         fclose(out);
616                         obj_outfile = outname;
617                 } else {
618                         FILE *tempf
619                                 = make_temp_file(obj_tfile, sizeof(obj_tfile), "cc", ".o");
620                         fclose(tempf);
621                         obj_outfile = obj_tfile;
622                 }
623
624                 assemble(obj_outfile, asm_tempfile);
625         }
626
627         /* link object file */
628         if(mode == CompileAssembleLink) {
629                 do_link(outname, obj_tfile);
630         }
631
632         obstack_free(&cppflags_obst, NULL);
633
634         exit_ast2firm();
635         exit_parser();
636         exit_ast();
637         exit_lexer();
638         exit_typehash();
639         exit_types();
640         exit_tokens();
641         exit_symbol_table();
642         return 0;
643 }