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