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