| // Copyright 2012 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| #include "a.h" | |
| /* | |
| * Helpers for building pkg/runtime. | |
| */ | |
| // mkzversion writes zversion.go: | |
| // | |
| // package runtime | |
| // const defaultGoroot = <goroot> | |
| // const theVersion = <version> | |
| // | |
| void | |
| mkzversion(char *dir, char *file) | |
| { | |
| Buf b, out; | |
| USED(dir); | |
| binit(&b); | |
| binit(&out); | |
| bwritestr(&out, bprintf(&b, | |
| "// auto generated by go tool dist\n" | |
| "\n" | |
| "package runtime\n" | |
| "\n" | |
| "const defaultGoroot = `%s`\n" | |
| "const theVersion = `%s`\n", goroot_final, goversion)); | |
| writefile(&out, file, 0); | |
| bfree(&b); | |
| bfree(&out); | |
| } | |
| // mkzgoarch writes zgoarch_$GOARCH.go: | |
| // | |
| // package runtime | |
| // const theGoarch = <goarch> | |
| // | |
| void | |
| mkzgoarch(char *dir, char *file) | |
| { | |
| Buf b, out; | |
| USED(dir); | |
| binit(&b); | |
| binit(&out); | |
| bwritestr(&out, bprintf(&b, | |
| "// auto generated by go tool dist\n" | |
| "\n" | |
| "package runtime\n" | |
| "\n" | |
| "const theGoarch = `%s`\n", goarch)); | |
| writefile(&out, file, 0); | |
| bfree(&b); | |
| bfree(&out); | |
| } | |
| // mkzgoos writes zgoos_$GOOS.go: | |
| // | |
| // package runtime | |
| // const theGoos = <goos> | |
| // | |
| void | |
| mkzgoos(char *dir, char *file) | |
| { | |
| Buf b, out; | |
| USED(dir); | |
| binit(&b); | |
| binit(&out); | |
| bwritestr(&out, bprintf(&b, | |
| "// auto generated by go tool dist\n" | |
| "\n" | |
| "package runtime\n" | |
| "\n" | |
| "const theGoos = `%s`\n", goos)); | |
| writefile(&out, file, 0); | |
| bfree(&b); | |
| bfree(&out); | |
| } | |
| static struct { | |
| char *goarch; | |
| char *goos; | |
| char *hdr; | |
| } zasmhdr[] = { | |
| {"386", "windows", | |
| "#define get_tls(r) MOVL 0x14(FS), r\n" | |
| "#define g(r) 0(r)\n" | |
| "#define m(r) 4(r)\n" | |
| }, | |
| {"386", "plan9", | |
| "#define get_tls(r) MOVL _tos(SB), r \n" | |
| "#define g(r) -8(r)\n" | |
| "#define m(r) -4(r)\n" | |
| }, | |
| {"386", "linux", | |
| "// On Linux systems, what we call 0(GS) and 4(GS) for g and m\n" | |
| "// turn into %gs:-8 and %gs:-4 (using gcc syntax to denote\n" | |
| "// what the machine sees as opposed to 8l input).\n" | |
| "// 8l rewrites 0(GS) and 4(GS) into these.\n" | |
| "//\n" | |
| "// On Linux Xen, it is not allowed to use %gs:-8 and %gs:-4\n" | |
| "// directly. Instead, we have to store %gs:0 into a temporary\n" | |
| "// register and then use -8(%reg) and -4(%reg). This kind\n" | |
| "// of addressing is correct even when not running Xen.\n" | |
| "//\n" | |
| "// 8l can rewrite MOVL 0(GS), CX into the appropriate pair\n" | |
| "// of mov instructions, using CX as the intermediate register\n" | |
| "// (safe because CX is about to be written to anyway).\n" | |
| "// But 8l cannot handle other instructions, like storing into 0(GS),\n" | |
| "// which is where these macros come into play.\n" | |
| "// get_tls sets up the temporary and then g and r use it.\n" | |
| "//\n" | |
| "// The final wrinkle is that get_tls needs to read from %gs:0,\n" | |
| "// but in 8l input it's called 8(GS), because 8l is going to\n" | |
| "// subtract 8 from all the offsets, as described above.\n" | |
| "#define get_tls(r) MOVL 8(GS), r\n" | |
| "#define g(r) -8(r)\n" | |
| "#define m(r) -4(r)\n" | |
| }, | |
| {"386", "", | |
| "#define get_tls(r)\n" | |
| "#define g(r) 0(GS)\n" | |
| "#define m(r) 4(GS)\n" | |
| }, | |
| {"amd64", "windows", | |
| "#define get_tls(r) MOVQ 0x28(GS), r\n" | |
| "#define g(r) 0(r)\n" | |
| "#define m(r) 8(r)\n" | |
| }, | |
| {"amd64", "", | |
| "// The offsets 0 and 8 are known to:\n" | |
| "// ../../cmd/6l/pass.c:/D_GS\n" | |
| "// cgo/gcc_linux_amd64.c:/^threadentry\n" | |
| "// cgo/gcc_darwin_amd64.c:/^threadentry\n" | |
| "//\n" | |
| "#define get_tls(r)\n" | |
| "#define g(r) 0(GS)\n" | |
| "#define m(r) 8(GS)\n" | |
| }, | |
| {"arm", "", | |
| "#define g R10\n" | |
| "#define m R9\n" | |
| "#define LR R14\n" | |
| }, | |
| }; | |
| // mkzasm writes zasm_$GOOS_$GOARCH.h, | |
| // which contains struct offsets for use by | |
| // assembly files. It also writes a copy to the work space | |
| // under the name zasm_GOOS_GOARCH.h (no expansion). | |
| // | |
| void | |
| mkzasm(char *dir, char *file) | |
| { | |
| int i, n; | |
| char *aggr, *p; | |
| Buf in, b, out; | |
| Vec argv, lines, fields; | |
| binit(&in); | |
| binit(&b); | |
| binit(&out); | |
| vinit(&argv); | |
| vinit(&lines); | |
| vinit(&fields); | |
| bwritestr(&out, "// auto generated by go tool dist\n\n"); | |
| for(i=0; i<nelem(zasmhdr); i++) { | |
| if(hasprefix(goarch, zasmhdr[i].goarch) && hasprefix(goos, zasmhdr[i].goos)) { | |
| bwritestr(&out, zasmhdr[i].hdr); | |
| goto ok; | |
| } | |
| } | |
| fatal("unknown $GOOS/$GOARCH in mkzasm"); | |
| ok: | |
| // Run 6c -DGOOS_goos -DGOARCH_goarch -Iworkdir -a proc.c | |
| // to get acid [sic] output. | |
| vreset(&argv); | |
| vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar)); | |
| vadd(&argv, bprintf(&b, "-DGOOS_%s", goos)); | |
| vadd(&argv, bprintf(&b, "-DGOARCH_%s", goarch)); | |
| vadd(&argv, bprintf(&b, "-I%s", workdir)); | |
| vadd(&argv, "-a"); | |
| vadd(&argv, "proc.c"); | |
| runv(&in, dir, CheckExit, &argv); | |
| // Convert input like | |
| // aggr G | |
| // { | |
| // Gobuf 24 sched; | |
| // 'Y' 48 stack0; | |
| // } | |
| // into output like | |
| // #define g_sched 24 | |
| // #define g_stack0 48 | |
| // | |
| aggr = nil; | |
| splitlines(&lines, bstr(&in)); | |
| for(i=0; i<lines.len; i++) { | |
| splitfields(&fields, lines.p[i]); | |
| if(fields.len == 2 && streq(fields.p[0], "aggr")) { | |
| if(streq(fields.p[1], "G")) | |
| aggr = "g"; | |
| else if(streq(fields.p[1], "M")) | |
| aggr = "m"; | |
| else if(streq(fields.p[1], "Gobuf")) | |
| aggr = "gobuf"; | |
| else if(streq(fields.p[1], "WinCall")) | |
| aggr = "wincall"; | |
| else if(streq(fields.p[1], "SEH")) | |
| aggr = "seh"; | |
| } | |
| if(hasprefix(lines.p[i], "}")) | |
| aggr = nil; | |
| if(aggr && hasprefix(lines.p[i], "\t") && fields.len >= 2) { | |
| n = fields.len; | |
| p = fields.p[n-1]; | |
| if(p[xstrlen(p)-1] == ';') | |
| p[xstrlen(p)-1] = '\0'; | |
| bwritestr(&out, bprintf(&b, "#define %s_%s %s\n", aggr, fields.p[n-1], fields.p[n-2])); | |
| } | |
| } | |
| // Write both to file and to workdir/zasm_GOOS_GOARCH.h. | |
| writefile(&out, file, 0); | |
| writefile(&out, bprintf(&b, "%s/zasm_GOOS_GOARCH.h", workdir), 0); | |
| bfree(&in); | |
| bfree(&b); | |
| bfree(&out); | |
| vfree(&argv); | |
| vfree(&lines); | |
| vfree(&fields); | |
| } | |
| static char *runtimedefs[] = { | |
| "proc.c", | |
| "iface.c", | |
| "hashmap.c", | |
| "chan.c", | |
| "parfor.c", | |
| }; | |
| // mkzruntimedefs writes zruntime_defs_$GOOS_$GOARCH.h, | |
| // which contains Go struct definitions equivalent to the C ones. | |
| // Mostly we just write the output of 6c -q to the file. | |
| // However, we run it on multiple files, so we have to delete | |
| // the duplicated definitions, and we don't care about the funcs | |
| // and consts, so we delete those too. | |
| // | |
| void | |
| mkzruntimedefs(char *dir, char *file) | |
| { | |
| int i, skip; | |
| char *p; | |
| Buf in, b, out; | |
| Vec argv, lines, fields, seen; | |
| binit(&in); | |
| binit(&b); | |
| binit(&out); | |
| vinit(&argv); | |
| vinit(&lines); | |
| vinit(&fields); | |
| vinit(&seen); | |
| bwritestr(&out, "// auto generated by go tool dist\n" | |
| "\n" | |
| "package runtime\n" | |
| "import \"unsafe\"\n" | |
| "var _ unsafe.Pointer\n" | |
| "\n" | |
| ); | |
| // Run 6c -DGOOS_goos -DGOARCH_goarch -Iworkdir -q | |
| // on each of the runtimedefs C files. | |
| vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar)); | |
| vadd(&argv, bprintf(&b, "-DGOOS_%s", goos)); | |
| vadd(&argv, bprintf(&b, "-DGOARCH_%s", goarch)); | |
| vadd(&argv, bprintf(&b, "-I%s", workdir)); | |
| vadd(&argv, "-q"); | |
| vadd(&argv, ""); | |
| p = argv.p[argv.len-1]; | |
| for(i=0; i<nelem(runtimedefs); i++) { | |
| argv.p[argv.len-1] = runtimedefs[i]; | |
| runv(&b, dir, CheckExit, &argv); | |
| bwriteb(&in, &b); | |
| } | |
| argv.p[argv.len-1] = p; | |
| // Process the aggregate output. | |
| skip = 0; | |
| splitlines(&lines, bstr(&in)); | |
| for(i=0; i<lines.len; i++) { | |
| p = lines.p[i]; | |
| // Drop comment, func, and const lines. | |
| if(hasprefix(p, "//") || hasprefix(p, "const") || hasprefix(p, "func")) | |
| continue; | |
| // Note beginning of type or var decl, which can be multiline. | |
| // Remove duplicates. The linear check of seen here makes the | |
| // whole processing quadratic in aggregate, but there are only | |
| // about 100 declarations, so this is okay (and simple). | |
| if(hasprefix(p, "type ") || hasprefix(p, "var ")) { | |
| splitfields(&fields, p); | |
| if(fields.len < 2) | |
| continue; | |
| if(find(fields.p[1], seen.p, seen.len) >= 0) { | |
| if(streq(fields.p[fields.len-1], "{")) | |
| skip = 1; // skip until } | |
| continue; | |
| } | |
| vadd(&seen, fields.p[1]); | |
| } | |
| if(skip) { | |
| if(hasprefix(p, "}")) | |
| skip = 0; | |
| continue; | |
| } | |
| bwritestr(&out, p); | |
| } | |
| writefile(&out, file, 0); | |
| bfree(&in); | |
| bfree(&b); | |
| bfree(&out); | |
| vfree(&argv); | |
| vfree(&lines); | |
| vfree(&fields); | |
| vfree(&seen); | |
| } |