Commit fd2bc95d authored by Russ Cox's avatar Russ Cox

[dev.cc] cmd/gc: changes for removing runtime C code

[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
parent 25f9f5d0
......@@ -7,6 +7,8 @@
#include "go.h"
#include "y.tab.h"
static NodeList *asmlist;
static void dumpexporttype(Type *t);
// Mark n's symbol as exported
......@@ -68,6 +70,11 @@ autoexport(Node *n, int ctxt)
// -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
......@@ -519,3 +526,37 @@ importtype(Type *pt, Type *t)
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);
}
......@@ -382,6 +382,7 @@ enum
SymExported = 1<<2, // already written out by export
SymUniq = 1<<3,
SymSiggen = 1<<4,
SymAsm = 1<<5,
};
struct Sym
......@@ -393,6 +394,7 @@ 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;
......@@ -860,6 +862,8 @@ EXTERN int32 lexlineno;
EXTERN int32 lineno;
EXTERN int32 prevlineno;
EXTERN Fmt pragcgobuf;
EXTERN char* infile;
EXTERN char* outfile;
EXTERN Biobuf* bout;
......@@ -890,6 +894,7 @@ EXTERN Pkg* typelinkpkg; // fake package for runtime type info (data)
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;
......@@ -897,6 +902,7 @@ extern char* unsafeimport;
EXTERN char* myimportpath;
EXTERN Idir* idirs;
EXTERN char* localimport;
EXTERN char* asmhdr;
EXTERN Type* types[NTYPE];
EXTERN Type* idealstring;
......@@ -1145,6 +1151,7 @@ void escapes(NodeList*);
*/
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);
......
......@@ -17,6 +17,8 @@ extern int yychar;
int yyprev;
int yylast;
static int imported_unsafe;
static void lexinit(void);
static void lexinit1(void);
static void lexfini(void);
......@@ -271,6 +273,9 @@ main(int argc, char *argv[])
flag_largemodel = 1;
setexp();
fmtstrinit(&pragcgobuf);
quotefmtinstall();
outfile = nil;
flagcount("+", "compiling runtime", &compiling_runtime);
......@@ -289,6 +294,7 @@ main(int argc, char *argv[])
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']);
......@@ -403,6 +409,8 @@ main(int argc, char *argv[])
block = 1;
iota = -1000000;
imported_unsafe = 0;
yyparse();
if(nsyntaxerrors != 0)
......@@ -509,6 +517,9 @@ main(int argc, char *argv[])
errorexit();
dumpobj();
if(asmhdr)
dumpasmhdr();
if(nerrors+nsavederrors)
errorexit();
......@@ -724,6 +735,7 @@ importfile(Val *f, int line)
}
importpkg = mkpkg(f->u.sval);
cannedimports("unsafe.6", unsafeimport);
imported_unsafe = 1;
return;
}
......@@ -1501,6 +1513,20 @@ caseout:
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
......@@ -1583,9 +1609,39 @@ go:
*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;
......@@ -1604,6 +1660,150 @@ out:
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)
{
......
......@@ -67,6 +67,16 @@ dumpobj(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");
......@@ -153,6 +163,8 @@ linksym(Sym *s)
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);
......
......@@ -1092,7 +1092,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
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;
......@@ -1105,7 +1105,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
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;
......@@ -1113,7 +1113,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
// 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;
......@@ -1123,15 +1123,8 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
// 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;
......@@ -1144,7 +1137,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
// 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++)
......
......@@ -1318,7 +1318,7 @@ gengcmask(Type *t, uint8 gcmask[16])
{
Bvec *vec;
vlong xoffset, nptr, i, j;
int half, mw;
int half;
uint8 bits, *pos;
memset(gcmask, 0, 16);
......@@ -1335,7 +1335,6 @@ gengcmask(Type *t, uint8 gcmask[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++) {
......@@ -1344,9 +1343,8 @@ gengcmask(Type *t, uint8 gcmask[16])
// 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;
......@@ -1525,11 +1523,9 @@ gengcprog1(ProgGen *g, Type *t, vlong *xoffset)
*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:
......
......@@ -3802,39 +3802,25 @@ checknil(Node *x, NodeList **init)
/*
* 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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment