[This CL is part of the removal of C code from package runtime.
See golang.org/s/dev.cc for an overview.]
export.c, lex.c:
Add -asmhdr flag to write assembly header file with struct
field offsets and const values. cmd/dist used to construct this
file by interpreting output from the C compiler.
Generate it from the Go definitions instead.
Also, generate the form we need directly, instead of relying
on cmd/dist for reprocessing.
lex.c, obj.c:
If the C compiler accepted #pragma cgo_xxx, recognize
a directive //go:cgo_xxx instead. The effect is the same as
in the C compiler: accumulate text into a buffer and emit in the
output file, where the linker will find and use it.
lex.c, obj.c:
Accept //go:linkname to control the external symbol name
used for a particular top-level Go variable. This makes it
possible to refer to C symbol names but also symbols from
other packages. It has always been possible to do this from
C and assembly. To drive home the point that this should not
be done lightly, require import "unsafe" in any file containing
//go:linkname.
plive.c, reflect.c, subr.c:
Hard-code that interfaces contain only pointers.
This means code handling multiword values in the garbage
collector and the stack copier can be deleted instead of being
converted. This change is already present in the dev.garbage
branch.
LGTM=r
R=r
CC=austin, golang-codereviews, iant, khr
https://golang.org/cl/
169360043
#include "go.h"
#include "y.tab.h"
+static NodeList *asmlist;
+
static void dumpexporttype(Type *t);
// Mark n's symbol as exported
// -A is for cmd/gc/mkbuiltin script, so export everything
if(debug['A'] || exportname(n->sym->name) || initname(n->sym->name))
exportsym(n);
+ if(asmhdr && n->sym->pkg == localpkg && !(n->sym->flags & SymAsm)) {
+ n->sym->flags |= SymAsm;
+ asmlist = list(asmlist, n);
+ }
+
}
static void
if(debug['E'])
print("import type %T %lT\n", pt, t);
}
+
+void
+dumpasmhdr(void)
+{
+ Biobuf *b;
+ NodeList *l;
+ Node *n;
+ Type *t;
+
+ b = Bopen(asmhdr, OWRITE);
+ if(b == nil)
+ fatal("open %s: %r", asmhdr);
+ Bprint(b, "// generated by %cg -asmhdr from package %s\n\n", thechar, localpkg->name);
+ for(l=asmlist; l; l=l->next) {
+ n = l->n;
+ if(isblanksym(n->sym))
+ continue;
+ switch(n->op) {
+ case OLITERAL:
+ Bprint(b, "#define const_%s %#V\n", n->sym->name, &n->val);
+ break;
+ case OTYPE:
+ t = n->type;
+ if(t->etype != TSTRUCT || t->map != T || t->funarg)
+ break;
+ for(t=t->type; t != T; t=t->down)
+ if(!isblanksym(t->sym))
+ Bprint(b, "#define %s_%s %d\n", n->sym->name, t->sym->name, (int)t->width);
+ break;
+ }
+ }
+
+ Bterm(b);
+}
SymExported = 1<<2, // already written out by export
SymUniq = 1<<3,
SymSiggen = 1<<4,
+ SymAsm = 1<<5,
};
struct Sym
int32 npkg; // number of imported packages with this name
uint32 uniqgen;
Pkg* importdef; // where imported definition was found
+ char* linkname; // link name
// saved and restored by dcopy
Pkg* pkg;
EXTERN int32 lineno;
EXTERN int32 prevlineno;
+EXTERN Fmt pragcgobuf;
+
EXTERN char* infile;
EXTERN char* outfile;
EXTERN Biobuf* bout;
EXTERN Pkg* weaktypepkg; // weak references to runtime type info
EXTERN Pkg* unsafepkg; // package unsafe
EXTERN Pkg* trackpkg; // fake package for field tracking
+EXTERN Pkg* rawpkg; // fake package for raw symbol names
EXTERN Pkg* phash[128];
EXTERN int tptr; // either TPTR32 or TPTR64
extern char* runtimeimport;
EXTERN char* myimportpath;
EXTERN Idir* idirs;
EXTERN char* localimport;
+EXTERN char* asmhdr;
EXTERN Type* types[NTYPE];
EXTERN Type* idealstring;
*/
void autoexport(Node *n, int ctxt);
void dumpexport(void);
+void dumpasmhdr(void);
int exportname(char *s);
void exportsym(Node *n);
void importconst(Sym *s, Type *t, Node *n);
int yyprev;
int yylast;
+static int imported_unsafe;
+
static void lexinit(void);
static void lexinit1(void);
static void lexfini(void);
flag_largemodel = 1;
setexp();
+
+ fmtstrinit(&pragcgobuf);
+ quotefmtinstall();
outfile = nil;
flagcount("+", "compiling runtime", &compiling_runtime);
flagcount("S", "print assembly listing", &debug['S']);
flagfn0("V", "print compiler version", doversion);
flagcount("W", "debug parse tree after type checking", &debug['W']);
+ flagstr("asmhdr", "file: write assembly header to named file", &asmhdr);
flagcount("complete", "compiling complete package (no C or assembly)", &pure_go);
flagstr("d", "list: print debug information about items in list", &debugstr);
flagcount("e", "no limit on number of errors reported", &debug['e']);
block = 1;
iota = -1000000;
+
+ imported_unsafe = 0;
yyparse();
if(nsyntaxerrors != 0)
errorexit();
dumpobj();
+
+ if(asmhdr)
+ dumpasmhdr();
if(nerrors+nsavederrors)
errorexit();
}
importpkg = mkpkg(f->u.sval);
cannedimports("unsafe.6", unsafeimport);
+ imported_unsafe = 1;
return;
}
return LLITERAL;
}
+static void pragcgo(char*);
+
+static int
+more(char **pp)
+{
+ char *p;
+
+ p = *pp;
+ while(yy_isspace(*p))
+ p++;
+ *pp = p;
+ return *p != '\0';
+}
+
/*
* read and interpret syntax that looks like
* //line parse.y:15
*cp++ = c;
}
*cp = 0;
+
+ if(strncmp(lexbuf, "go:cgo_", 7) == 0)
+ pragcgo(lexbuf);
+
ep = strchr(lexbuf, ' ');
if(ep != nil)
*ep = 0;
+
+ if(strcmp(lexbuf, "go:linkname") == 0) {
+ if(!imported_unsafe)
+ yyerror("//go:linkname only allowed in Go files that import \"unsafe\"");
+ if(ep == nil) {
+ yyerror("usage: //go:linkname localname linkname");
+ goto out;
+ }
+ cp = ep+1;
+ while(yy_isspace(*cp))
+ cp++;
+ ep = strchr(cp, ' ');
+ if(ep == nil) {
+ yyerror("usage: //go:linkname localname linkname");
+ goto out;
+ }
+ *ep++ = 0;
+ while(yy_isspace(*ep))
+ ep++;
+ if(*ep == 0) {
+ yyerror("usage: //go:linkname localname linkname");
+ goto out;
+ }
+ lookup(cp)->linkname = strdup(ep);
+ goto out;
+ }
if(strcmp(lexbuf, "go:nointerface") == 0 && fieldtrack_enabled) {
nointerface = 1;
return c;
}
+static char*
+getimpsym(char **pp)
+{
+ char *p, *start;
+
+ more(pp); // skip spaces
+
+ p = *pp;
+ if(*p == '\0' || *p == '"')
+ return nil;
+
+ start = p;
+ while(*p != '\0' && !yy_isspace(*p) && *p != '"')
+ p++;
+ if(*p != '\0')
+ *p++ = '\0';
+
+ *pp = p;
+ return start;
+}
+
+static char*
+getquoted(char **pp)
+{
+ char *p, *start;
+
+ more(pp); // skip spaces
+
+ p = *pp;
+ if(*p != '"')
+ return nil;
+ p++;
+
+ start = p;
+ while(*p != '"') {
+ if(*p == '\0')
+ return nil;
+ p++;
+ }
+ *p++ = '\0';
+ *pp = p;
+ return start;
+}
+
+// Copied nearly verbatim from the C compiler's #pragma parser.
+// TODO: Rewrite more cleanly once the compiler is written in Go.
+static void
+pragcgo(char *text)
+{
+ char *local, *remote, *p, *q, *verb;
+
+ for(q=text; *q != '\0' && *q != ' '; q++)
+ ;
+ if(*q == ' ')
+ *q++ = '\0';
+
+ verb = text+3; // skip "go:"
+
+ if(strcmp(verb, "cgo_dynamic_linker") == 0 || strcmp(verb, "dynlinker") == 0) {
+ p = getquoted(&q);
+ if(p == nil)
+ goto err1;
+ fmtprint(&pragcgobuf, "cgo_dynamic_linker %q\n", p);
+ goto out;
+
+ err1:
+ yyerror("usage: //go:cgo_dynamic_linker \"path\"");
+ goto out;
+ }
+
+ if(strcmp(verb, "dynexport") == 0)
+ verb = "cgo_export_dynamic";
+ if(strcmp(verb, "cgo_export_static") == 0 || strcmp(verb, "cgo_export_dynamic") == 0) {
+ local = getimpsym(&q);
+ if(local == nil)
+ goto err2;
+ if(!more(&q)) {
+ fmtprint(&pragcgobuf, "%s %q\n", verb, local);
+ goto out;
+ }
+ remote = getimpsym(&q);
+ if(remote == nil)
+ goto err2;
+ fmtprint(&pragcgobuf, "%s %q %q\n", verb, local, remote);
+ goto out;
+
+ err2:
+ yyerror("usage: //go:%s local [remote]", verb);
+ goto out;
+ }
+
+ if(strcmp(verb, "cgo_import_dynamic") == 0 || strcmp(verb, "dynimport") == 0) {
+ local = getimpsym(&q);
+ if(local == nil)
+ goto err3;
+ if(!more(&q)) {
+ fmtprint(&pragcgobuf, "cgo_import_dynamic %q\n", local);
+ goto out;
+ }
+ remote = getimpsym(&q);
+ if(remote == nil)
+ goto err3;
+ if(!more(&q)) {
+ fmtprint(&pragcgobuf, "cgo_import_dynamic %q %q\n", local, remote);
+ goto out;
+ }
+ p = getquoted(&q);
+ if(p == nil)
+ goto err3;
+ fmtprint(&pragcgobuf, "cgo_import_dynamic %q %q %q\n", local, remote, p);
+ goto out;
+
+ err3:
+ yyerror("usage: //go:cgo_import_dynamic local [remote [\"library\"]]");
+ goto out;
+ }
+
+ if(strcmp(verb, "cgo_import_static") == 0) {
+ local = getimpsym(&q);
+ if(local == nil || more(&q))
+ goto err4;
+ fmtprint(&pragcgobuf, "cgo_import_static %q\n", local);
+ goto out;
+
+ err4:
+ yyerror("usage: //go:cgo_import_static local");
+ goto out;
+ }
+
+ if(strcmp(verb, "cgo_ldflag") == 0) {
+ p = getquoted(&q);
+ if(p == nil)
+ goto err5;
+ fmtprint(&pragcgobuf, "cgo_ldflag %q\n", p);
+ goto out;
+
+ err5:
+ yyerror("usage: //go:cgo_ldflag \"arg\"");
+ goto out;
+ }
+
+out:;
+}
+
int32
yylex(void)
{
startobj = Boffset(bout);
Bprint(bout, "go object %s %s %s %s\n", getgoos(), getgoarch(), getgoversion(), expstring());
}
+
+ if(pragcgobuf.to > pragcgobuf.start) {
+ if(writearchive) {
+ // write empty export section; must be before cgo section
+ Bprint(bout, "\n$$\n\n$$\n\n");
+ }
+ Bprint(bout, "\n$$ // cgo\n");
+ Bprint(bout, "%s\n$$\n\n", fmtstrflush(&pragcgobuf));
+ }
+
Bprint(bout, "\n!\n");
return s->lsym;
if(isblanksym(s))
s->lsym = linklookup(ctxt, "_", 0);
+ else if(s->linkname != nil)
+ s->lsym = linklookup(ctxt, s->linkname, 0);
else {
p = smprint("%s.%s", s->pkg->prefix, s->name);
s->lsym = linklookup(ctxt, p, 0);
case TCOMPLEX64:
case TCOMPLEX128:
for(i = 0; i < t->width; i++) {
- bvset(bv, ((*xoffset + i) / widthptr) * BitsPerPointer); // 1 = live scalar
+ bvset(bv, ((*xoffset + i) / widthptr) * BitsPerPointer); // 1 = live scalar (BitsScalar)
}
*xoffset += t->width;
break;
case TMAP:
if((*xoffset & (widthptr-1)) != 0)
fatal("twobitwalktype1: invalid alignment, %T", t);
- bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr (BitsPointer)
*xoffset += t->width;
break;
// struct { byte *str; intgo len; }
if((*xoffset & (widthptr-1)) != 0)
fatal("twobitwalktype1: invalid alignment, %T", t);
- bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot (BitsPointer)
*xoffset += t->width;
break;
// struct { Type *type; union { void *ptr, uintptr val } data; }
if((*xoffset & (widthptr-1)) != 0)
fatal("twobitwalktype1: invalid alignment, %T", t);
- bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 0);
- bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 1); // 3 = multiword
- // next word contains 2 = Iface, 3 = Eface
- if(isnilinter(t)) {
- bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 2);
- bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 3);
- } else {
- bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 3);
- }
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot (BitsPointer)
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 3); // 2 = live ptr in second slot (BitsPointer)
*xoffset += t->width;
break;
// struct { byte *array; uintgo len; uintgo cap; }
if((*xoffset & (widthptr-1)) != 0)
fatal("twobitwalktype1: invalid TARRAY alignment, %T", t);
- bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot (BitsPointer)
*xoffset += t->width;
} else
for(i = 0; i < t->bound; i++)
{
Bvec *vec;
vlong xoffset, nptr, i, j;
- int half, mw;
+ int half;
uint8 bits, *pos;
memset(gcmask, 0, 16);
pos = (uint8*)gcmask;
nptr = (t->width+widthptr-1)/widthptr;
half = 0;
- mw = 0;
// If number of words is odd, repeat the mask.
// This makes simpler handling of arrays in runtime.
for(j=0; j<=(nptr%2); j++) {
// Some fake types (e.g. Hmap) has missing fileds.
// twobitwalktype1 generates BitsDead for that holes,
// replace BitsDead with BitsScalar.
- if(!mw && bits == BitsDead)
+ if(bits == BitsDead)
bits = BitsScalar;
- mw = !mw && bits == BitsMultiWord;
bits <<= 2;
if(half)
bits <<= 4;
*xoffset += t->width;
break;
case TINTER:
- proggendata(g, BitsMultiWord);
- if(isnilinter(t))
- proggendata(g, BitsEface);
- else
- proggendata(g, BitsIface);
+ // Assuming IfacePointerOnly=1.
+ proggendata(g, BitsPointer);
+ proggendata(g, BitsPointer);
*xoffset += t->width;
break;
case TARRAY:
/*
* Can this type be stored directly in an interface word?
+ * Yes, if the representation is a single pointer.
*/
int
isdirectiface(Type *t)
{
- // Setting IfacePointerOnly = 1 changes the
- // interface representation so that the data word
- // in an interface value must always be a pointer.
- // Setting it to 0 uses the original representation,
- // where the data word can hold a pointer or any
- // non-pointer value no bigger than a pointer.
- enum {
- IfacePointerOnly = 1,
- };
-
- if(IfacePointerOnly) {
- switch(t->etype) {
- case TPTR32:
- case TPTR64:
- case TCHAN:
- case TMAP:
- case TFUNC:
- case TUNSAFEPTR:
- return 1;
- case TARRAY:
- // Array of 1 direct iface type can be direct.
- return t->bound == 1 && isdirectiface(t->type);
- case TSTRUCT:
- // Struct with 1 field of direct iface type can be direct.
- return t->type != T && t->type->down == T && isdirectiface(t->type->type);
- }
- return 0;
+ switch(t->etype) {
+ case TPTR32:
+ case TPTR64:
+ case TCHAN:
+ case TMAP:
+ case TFUNC:
+ case TUNSAFEPTR:
+ return 1;
+ case TARRAY:
+ // Array of 1 direct iface type can be direct.
+ return t->bound == 1 && isdirectiface(t->type);
+ case TSTRUCT:
+ // Struct with 1 field of direct iface type can be direct.
+ return t->type != T && t->type->down == T && isdirectiface(t->type->type);
}
-
- dowidth(t);
- return t->width <= widthptr;
+ return 0;
}