]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.cc] cmd/gc: changes for removing runtime C code
authorRuss Cox <rsc@golang.org>
Tue, 11 Nov 2014 06:27:30 +0000 (01:27 -0500)
committerRuss Cox <rsc@golang.org>
Tue, 11 Nov 2014 06:27:30 +0000 (01:27 -0500)
[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

src/cmd/gc/export.c
src/cmd/gc/go.h
src/cmd/gc/lex.c
src/cmd/gc/obj.c
src/cmd/gc/plive.c
src/cmd/gc/reflect.c
src/cmd/gc/subr.c

index da5984cebf864958bd29c4b0acfe78b8b06c8000..aeee552362f2826bb7a4edf1626366f731869455 100644 (file)
@@ -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);
+}
index 965a0550d3683b64c097e8a6f07bb586d4180504..92625f91921ee68f2af16a9e1f58aa4077833d88 100644 (file)
@@ -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);
index 523ba37aa7b1ed1d99d477bca5a0aa6407436ac8..2bd7adfb64e57e579c5ae84d7197ab87c8ed93b6 100644 (file)
@@ -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)
 {
index b752a13ced5cdc8f8b37dd35027fa96e48cb26ea..7e4e97854a8748e8c40c56daed1f9abffb42bd97 100644 (file)
@@ -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);
index 0feb2c710ac439dc736cdaa820bdb7c019a92821..3bfa69b1f02084078f8e1ea6b7444bc08d858933 100644 (file)
@@ -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++)
index b2ff2fbc5efa012a9f61868ea7b06b9f24600371..4155953be1238b7e1d02a5beff03a089f9a30c28 100644 (file)
@@ -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:
index c3bc5af3b8d888c98bbf0f96980c279f02b111fa..5e369b69578a22f6ff774976215fae49af6464bb 100644 (file)
@@ -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;
 }