OSDN Git Service

libgo/runtime: Change std::abort to abort.
[pf3gnuchains/gcc-fork.git] / libgo / runtime / goc2c.c
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 /* Translate a .goc file into a .c file.  A .goc file is a combination
6    of a limited form of Go with C.  */
7
8 /*
9    package PACKAGENAME
10    {# line}
11    func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] \{
12      C code with proper brace nesting
13    \}
14 */
15
16 /* We generate C code which implements the function such that it can
17    be called from Go and executes the C code.  */
18
19 #include <assert.h>
20 #include <ctype.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25
26 /* Whether we're emitting for gcc */
27 static int gcc;
28
29 /* Package prefix to use; only meaningful for gcc */
30 static const char *prefix;
31
32 /* File and line number */
33 static const char *file;
34 static unsigned int lineno = 1;
35
36 /* List of names and types.  */
37 struct params {
38         struct params *next;
39         char *name;
40         char *type;
41 };
42
43 /* index into type_table */
44 enum {
45         Bool,
46         Float,
47         Int,
48         Uint,
49         Uintptr,
50         String,
51         Slice,
52         Eface,
53 };
54
55 static struct {
56         char *name;
57         int size;
58 } type_table[] = {
59         /* variable sized first, for easy replacement */
60         /* order matches enum above */
61         /* default is 32-bit architecture sizes */
62         "bool",         1,
63         "float",        4,
64         "int",          4,
65         "uint",         4,
66         "uintptr",      4,
67         "String",       8,
68         "Slice",        12,
69         "Eface",        8,
70
71         /* fixed size */
72         "float32",      4,
73         "float64",      8,
74         "byte",         1,
75         "int8",         1,
76         "uint8",        1,
77         "int16",        2,
78         "uint16",       2,
79         "int32",        4,
80         "uint32",       4,
81         "int64",        8,
82         "uint64",       8,
83
84         NULL,
85 };
86
87 /* Fixed structure alignment (non-gcc only) */
88 int structround = 4;
89
90 /* Unexpected EOF.  */
91 static void
92 bad_eof(void)
93 {
94         fprintf(stderr, "%s:%u: unexpected EOF\n", file, lineno);
95         exit(1);
96 }
97
98 /* Out of memory.  */
99 static void
100 bad_mem(void)
101 {
102         fprintf(stderr, "%s:%u: out of memory\n", file, lineno);
103         exit(1);
104 }
105
106 /* Allocate memory without fail.  */
107 static void *
108 xmalloc(unsigned int size)
109 {
110         void *ret = malloc(size);
111         if (ret == NULL)
112                 bad_mem();
113         return ret;
114 }
115
116 /* Reallocate memory without fail.  */
117 static void*
118 xrealloc(void *buf, unsigned int size)
119 {
120         void *ret = realloc(buf, size);
121         if (ret == NULL)
122                 bad_mem();
123         return ret;
124 }
125
126 /* Free a list of parameters.  */
127 static void
128 free_params(struct params *p)
129 {
130         while (p != NULL) {
131                 struct params *next;
132
133                 next = p->next;
134                 free(p->name);
135                 free(p->type);
136                 free(p);
137                 p = next;
138         }
139 }
140
141 /* Read a character, tracking lineno.  */
142 static int
143 getchar_update_lineno(void)
144 {
145         int c;
146
147         c = getchar();
148         if (c == '\n')
149                 ++lineno;
150         return c;
151 }
152
153 /* Read a character, giving an error on EOF, tracking lineno.  */
154 static int
155 getchar_no_eof(void)
156 {
157         int c;
158
159         c = getchar_update_lineno();
160         if (c == EOF)
161                 bad_eof();
162         return c;
163 }
164
165 /* Read a character, skipping comments.  */
166 static int
167 getchar_skipping_comments(void)
168 {
169         int c;
170
171         while (1) {
172                 c = getchar_update_lineno();
173                 if (c != '/')
174                         return c;
175
176                 c = getchar();
177                 if (c == '/') {
178                         do {
179                                 c = getchar_update_lineno();
180                         } while (c != EOF && c != '\n');
181                         return c;
182                 } else if (c == '*') {
183                         while (1) {
184                                 c = getchar_update_lineno();
185                                 if (c == EOF)
186                                         return EOF;
187                                 if (c == '*') {
188                                         do {
189                                                 c = getchar_update_lineno();
190                                         } while (c == '*');
191                                         if (c == '/')
192                                                 break;
193                                 }
194                         }
195                 } else {
196                         ungetc(c, stdin);
197                         return '/';
198                 }
199         }
200 }
201
202 /* Read and return a token.  Tokens are delimited by whitespace or by
203    [(),{}].  The latter are all returned as single characters.  */
204 static char *
205 read_token(void)
206 {
207         int c;
208         char *buf;
209         unsigned int alc, off;
210         const char* delims = "(),{}";
211
212         while (1) {
213                 c = getchar_skipping_comments();
214                 if (c == EOF)
215                         return NULL;
216                 if (!isspace(c))
217                         break;
218         }
219         alc = 16;
220         buf = xmalloc(alc + 1);
221         off = 0;
222         if (strchr(delims, c) != NULL) {
223                 buf[off] = c;
224                 ++off;
225         } else {
226                 while (1) {
227                         if (off >= alc) {
228                                 alc *= 2;
229                                 buf = xrealloc(buf, alc + 1);
230                         }
231                         buf[off] = c;
232                         ++off;
233                         c = getchar_skipping_comments();
234                         if (c == EOF)
235                                 break;
236                         if (isspace(c) || strchr(delims, c) != NULL) {
237                                 if (c == '\n')
238                                         lineno--;
239                                 ungetc(c, stdin);
240                                 break;
241                         }
242                 }
243         }
244         buf[off] = '\0';
245         return buf;
246 }
247
248 /* Read a token, giving an error on EOF.  */
249 static char *
250 read_token_no_eof(void)
251 {
252         char *token = read_token();
253         if (token == NULL)
254                 bad_eof();
255         return token;
256 }
257
258 /* Read the package clause, and return the package name.  */
259 static char *
260 read_package(void)
261 {
262         char *token;
263
264         token = read_token_no_eof();
265         if (strcmp(token, "package") != 0) {
266                 fprintf(stderr,
267                         "%s:%u: expected \"package\", got \"%s\"\n",
268                         file, lineno, token);
269                 exit(1);
270         }
271         return read_token_no_eof();
272 }
273
274 /* Read and copy preprocessor lines.  */
275 static void
276 read_preprocessor_lines(void)
277 {
278         while (1) {
279                 int c;
280
281                 do {
282                         c = getchar_skipping_comments();
283                 } while (isspace(c));
284                 if (c != '#') {
285                         ungetc(c, stdin);
286                         break;
287                 }
288                 putchar(c);
289                 do {
290                         c = getchar_update_lineno();
291                         putchar(c);
292                 } while (c != '\n');
293         }
294 }
295
296 /* Read a type in Go syntax and return a type in C syntax.  We only
297    permit basic types and pointers.  */
298 static char *
299 read_type(void)
300 {
301         char *p, *op, *q;
302         int pointer_count;
303         unsigned int len;
304
305         p = read_token_no_eof();
306         if (*p != '*')
307                 return p;
308         op = p;
309         pointer_count = 0;
310         while (*p == '*') {
311                 ++pointer_count;
312                 ++p;
313         }
314         len = strlen(p);
315         q = xmalloc(len + pointer_count + 1);
316         memcpy(q, p, len);
317         while (pointer_count > 0) {
318                 q[len] = '*';
319                 ++len;
320                 --pointer_count;
321         }
322         q[len] = '\0';
323         free(op);
324         return q;
325 }
326
327 /* Return the size of the given type. */
328 static int
329 type_size(char *p)
330 {
331         int i;
332
333         if(p[strlen(p)-1] == '*')
334                 return type_table[Uintptr].size;
335
336         for(i=0; type_table[i].name; i++)
337                 if(strcmp(type_table[i].name, p) == 0)
338                         return type_table[i].size;
339         if(!gcc) {
340                 fprintf(stderr, "%s:%u: unknown type %s\n", file, lineno, p);
341                 exit(1);
342         }
343         return 1;
344 }
345
346 /* Read a list of parameters.  Each parameter is a name and a type.
347    The list ends with a ')'.  We have already read the '('.  */
348 static struct params *
349 read_params(int *poffset)
350 {
351         char *token;
352         struct params *ret, **pp, *p;
353         int offset, size, rnd;
354
355         ret = NULL;
356         pp = &ret;
357         token = read_token_no_eof();
358         offset = 0;
359         if (strcmp(token, ")") != 0) {
360                 while (1) {
361                         p = xmalloc(sizeof(struct params));
362                         p->name = token;
363                         p->type = read_type();
364                         p->next = NULL;
365                         *pp = p;
366                         pp = &p->next;
367
368                         size = type_size(p->type);
369                         rnd = size;
370                         if(rnd > structround)
371                                 rnd = structround;
372                         if(offset%rnd)
373                                 offset += rnd - offset%rnd;
374                         offset += size;
375
376                         token = read_token_no_eof();
377                         if (strcmp(token, ",") != 0)
378                                 break;
379                         token = read_token_no_eof();
380                 }
381         }
382         if (strcmp(token, ")") != 0) {
383                 fprintf(stderr, "%s:%u: expected '('\n",
384                         file, lineno);
385                 exit(1);
386         }
387         if (poffset != NULL)
388                 *poffset = offset;
389         return ret;
390 }
391
392 /* Read a function header.  This reads up to and including the initial
393    '{' character.  Returns 1 if it read a header, 0 at EOF.  */
394 static int
395 read_func_header(char **name, struct params **params, int *paramwid, struct params **rets)
396 {
397         int lastline;
398         char *token;
399
400         lastline = -1;
401         while (1) {
402                 token = read_token();
403                 if (token == NULL)
404                         return 0;
405                 if (strcmp(token, "func") == 0) {
406                         if(lastline != -1)
407                                 printf("\n");
408                         break;
409                 }
410                 if (lastline != lineno) {
411                         if (lastline == lineno-1)
412                                 printf("\n");
413                         else
414                                 printf("\n#line %d \"%s\"\n", lineno, file);
415                         lastline = lineno;
416                 }
417                 printf("%s ", token);
418         }
419
420         *name = read_token_no_eof();
421
422         token = read_token();
423         if (token == NULL || strcmp(token, "(") != 0) {
424                 fprintf(stderr, "%s:%u: expected \"(\"\n",
425                         file, lineno);
426                 exit(1);
427         }
428         *params = read_params(paramwid);
429
430         token = read_token();
431         if (token == NULL || strcmp(token, "(") != 0)
432                 *rets = NULL;
433         else {
434                 *rets = read_params(NULL);
435                 token = read_token();
436         }
437         if (token == NULL || strcmp(token, "{") != 0) {
438                 fprintf(stderr, "%s:%u: expected \"{\"\n",
439                         file, lineno);
440                 exit(1);
441         }
442         return 1;
443 }
444
445 /* Write out parameters.  */
446 static void
447 write_params(struct params *params, int *first)
448 {
449         struct params *p;
450
451         for (p = params; p != NULL; p = p->next) {
452                 if (*first)
453                         *first = 0;
454                 else
455                         printf(", ");
456                 printf("%s %s", p->type, p->name);
457         }
458 }
459
460 /* Write a 6g function header.  */
461 static void
462 write_6g_func_header(char *package, char *name, struct params *params,
463                      int paramwid, struct params *rets)
464 {
465         int first, n;
466
467         printf("void\n%s·%s(", package, name);
468         first = 1;
469         write_params(params, &first);
470
471         /* insert padding to align output struct */
472         if(rets != NULL && paramwid%structround != 0) {
473                 n = structround - paramwid%structround;
474                 if(n & 1)
475                         printf(", uint8");
476                 if(n & 2)
477                         printf(", uint16");
478                 if(n & 4)
479                         printf(", uint32");
480         }
481
482         write_params(rets, &first);
483         printf(")\n{\n");
484 }
485
486 /* Write a 6g function trailer.  */
487 static void
488 write_6g_func_trailer(struct params *rets)
489 {
490         struct params *p;
491
492         for (p = rets; p != NULL; p = p->next)
493                 printf("\tFLUSH(&%s);\n", p->name);
494         printf("}\n");
495 }
496
497 /* Define the gcc function return type if necessary.  */
498 static void
499 define_gcc_return_type(char *package, char *name, struct params *rets)
500 {
501         struct params *p;
502
503         if (rets == NULL || rets->next == NULL)
504                 return;
505         printf("struct %s_%s_ret {\n", package, name);
506         for (p = rets; p != NULL; p = p->next)
507                 printf("  %s %s;\n", p->type, p->name);
508         printf("};\n");
509 }
510
511 /* Write out the gcc function return type.  */
512 static void
513 write_gcc_return_type(char *package, char *name, struct params *rets)
514 {
515         if (rets == NULL)
516                 printf("void");
517         else if (rets->next == NULL)
518                 printf("%s", rets->type);
519         else
520                 printf("struct %s_%s_ret", package, name);
521 }
522
523 /* Write out a gcc function header.  */
524 static void
525 write_gcc_func_header(char *package, char *name, struct params *params,
526                       struct params *rets)
527 {
528         int first;
529         struct params *p;
530
531         define_gcc_return_type(package, name, rets);
532         write_gcc_return_type(package, name, rets);
533         printf(" %s_%s(", package, name);
534         first = 1;
535         write_params(params, &first);
536         printf(") asm (\"");
537         if (prefix != NULL)
538           printf("%s.", prefix);
539         printf("%s.%s\");\n", package, name);
540         write_gcc_return_type(package, name, rets);
541         printf(" %s_%s(", package, name);
542         first = 1;
543         write_params(params, &first);
544         printf(")\n{\n");
545         for (p = rets; p != NULL; p = p->next)
546                 printf("  %s %s;\n", p->type, p->name);
547 }
548
549 /* Write out a gcc function trailer.  */
550 static void
551 write_gcc_func_trailer(char *package, char *name, struct params *rets)
552 {
553         if (rets == NULL)
554                 ;
555         else if (rets->next == NULL)
556                 printf("return %s;\n", rets->name);
557         else {
558                 struct params *p;
559
560                 printf("  {\n    struct %s_%s_ret __ret;\n", package, name);
561                 for (p = rets; p != NULL; p = p->next)
562                         printf("    __ret.%s = %s;\n", p->name, p->name);
563                 printf("    return __ret;\n  }\n");
564         }
565         printf("}\n");
566 }
567
568 /* Write out a function header.  */
569 static void
570 write_func_header(char *package, char *name,
571                   struct params *params, int paramwid,
572                   struct params *rets)
573 {
574         if (gcc)
575                 write_gcc_func_header(package, name, params, rets);
576         else
577                 write_6g_func_header(package, name, params, paramwid, rets);
578         printf("#line %d \"%s\"\n", lineno, file);
579 }
580
581 /* Write out a function trailer.  */
582 static void
583 write_func_trailer(char *package, char *name,
584                    struct params *rets)
585 {
586         if (gcc)
587                 write_gcc_func_trailer(package, name, rets);
588         else
589                 write_6g_func_trailer(rets);
590 }
591
592 /* Read and write the body of the function, ending in an unnested }
593    (which is read but not written).  */
594 static void
595 copy_body(void)
596 {
597         int nesting = 0;
598         while (1) {
599                 int c;
600
601                 c = getchar_no_eof();
602                 if (c == '}' && nesting == 0)
603                         return;
604                 putchar(c);
605                 switch (c) {
606                 default:
607                         break;
608                 case '{':
609                         ++nesting;
610                         break;
611                 case '}':
612                         --nesting;
613                         break;
614                 case '/':
615                         c = getchar_update_lineno();
616                         putchar(c);
617                         if (c == '/') {
618                                 do {
619                                         c = getchar_no_eof();
620                                         putchar(c);
621                                 } while (c != '\n');
622                         } else if (c == '*') {
623                                 while (1) {
624                                         c = getchar_no_eof();
625                                         putchar(c);
626                                         if (c == '*') {
627                                                 do {
628                                                         c = getchar_no_eof();
629                                                         putchar(c);
630                                                 } while (c == '*');
631                                                 if (c == '/')
632                                                         break;
633                                         }
634                                 }
635                         }
636                         break;
637                 case '"':
638                 case '\'':
639                         {
640                                 int delim = c;
641                                 do {
642                                         c = getchar_no_eof();
643                                         putchar(c);
644                                         if (c == '\\') {
645                                                 c = getchar_no_eof();
646                                                 putchar(c);
647                                                 c = '\0';
648                                         }
649                                 } while (c != delim);
650                         }
651                         break;
652                 }
653         }
654 }
655
656 /* Process the entire file.  */
657 static void
658 process_file(void)
659 {
660         char *package, *name;
661         struct params *params, *rets;
662         int paramwid;
663
664         package = read_package();
665         read_preprocessor_lines();
666         while (read_func_header(&name, &params, &paramwid, &rets)) {
667                 write_func_header(package, name, params, paramwid, rets);
668                 copy_body();
669                 write_func_trailer(package, name, rets);
670                 free(name);
671                 free_params(params);
672                 free_params(rets);
673         }
674         free(package);
675 }
676
677 static void
678 usage(void)
679 {
680         fprintf(stderr, "Usage: goc2c [--6g | --gc] [--go-prefix PREFIX] [file]\n");
681         exit(1);
682 }
683
684 int
685 main(int argc, char **argv)
686 {
687         char *goarch;
688
689         while(argc > 1 && argv[1][0] == '-') {
690                 if(strcmp(argv[1], "-") == 0)
691                         break;
692                 if(strcmp(argv[1], "--6g") == 0)
693                         gcc = 0;
694                 else if(strcmp(argv[1], "--gcc") == 0)
695                         gcc = 1;
696                 else if (strcmp(argv[1], "--go-prefix") == 0 && argc > 2) {
697                         prefix = argv[2];
698                         argc--;
699                         argv++;
700                 } else
701                         usage();
702                 argc--;
703                 argv++;
704         }
705
706         if(argc <= 1 || strcmp(argv[1], "-") == 0) {
707                 file = "<stdin>";
708                 process_file();
709                 return 0;
710         }
711
712         if(argc > 2)
713                 usage();
714
715         file = argv[1];
716         if(freopen(file, "r", stdin) == 0) {
717                 fprintf(stderr, "open %s: %s\n", file, strerror(errno));
718                 exit(1);
719         }
720
721         if(!gcc) {
722                 // 6g etc; update size table
723                 goarch = getenv("GOARCH");
724                 if(goarch != NULL && strcmp(goarch, "amd64") == 0) {
725                         type_table[Uintptr].size = 8;
726                         type_table[String].size = 16;
727                         type_table[Slice].size = 8+4+4;
728                         type_table[Eface].size = 8+8;
729                         structround = 8;
730                 }
731         }
732
733         process_file();
734         return 0;
735 }