OSDN Git Service

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