re PR go/52358 (math FAILs on Solaris 8 and 9)
[gcc.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 }