]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/cgo: improve gccgo support
authorIan Lance Taylor <iant@golang.org>
Thu, 1 Nov 2012 18:21:30 +0000 (11:21 -0700)
committerIan Lance Taylor <iant@golang.org>
Thu, 1 Nov 2012 18:21:30 +0000 (11:21 -0700)
Use wrapper functions to tell scheduler what we are doing.

With this patch, and a separate patch to the go tool, all the
cgo tests pass with gccgo.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6812058

src/cmd/cgo/gcc.go
src/cmd/cgo/out.go

index 689c6eb0672e4c2a4b2d4c7f6f4a276c8012295c..191751c40ef2a580cc5a54300e3230334f20b145 100644 (file)
@@ -644,7 +644,14 @@ func (p *Package) rewriteRef(f *File) {
                        n.Kind = "var"
                }
                if n.Mangle == "" {
-                       n.Mangle = "_C" + n.Kind + "_" + n.Go
+                       // When using gccgo variables have to be
+                       // exported so that they become global symbols
+                       // that the C code can refer to.
+                       prefix := "_C"
+                       if *gccgo && n.Kind == "var" {
+                               prefix = "C"
+                       }
+                       n.Mangle = prefix + n.Kind + "_" + n.Go
                }
        }
 
index 941d1f64ca050aba633d873d8b9daa96ef8d7afd..58ac92fa3180df3a9512712c258cdb04c2f90dae 100644 (file)
@@ -27,6 +27,8 @@ func (p *Package) writeDefs() {
        fc := creat(*objDir + "_cgo_defun.c")
        fm := creat(*objDir + "_cgo_main.c")
 
+       var gccgoInit bytes.Buffer
+
        fflg := creat(*objDir + "_cgo_flags")
        for k, v := range p.CgoFlags {
                fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v)
@@ -81,6 +83,8 @@ func (p *Package) writeDefs() {
                fmt.Fprintf(fc, cProlog)
        }
 
+       gccgoSymbolPrefix := p.gccgoSymbolPrefix()
+
        cVars := make(map[string]bool)
        for _, key := range nameKeys(p.Name) {
                n := p.Name[key]
@@ -97,7 +101,12 @@ func (p *Package) writeDefs() {
                        cVars[n.C] = true
                }
 
-               fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C)
+               if *gccgo {
+                       fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle)
+                       fmt.Fprintf(&gccgoInit, "\t%s = &%s;\n", n.Mangle, n.C)
+               } else {
+                       fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C)
+               }
                fmt.Fprintf(fc, "\n")
 
                fmt.Fprintf(fgo2, "var %s ", n.Mangle)
@@ -127,6 +136,14 @@ func (p *Package) writeDefs() {
                p.writeExports(fgo2, fc, fm)
        }
 
+       init := gccgoInit.String()
+       if init != "" {
+               fmt.Fprintln(fc, "static void init(void) __attribute__ ((constructor));")
+               fmt.Fprintln(fc, "static void init(void) {")
+               fmt.Fprint(fc, init)
+               fmt.Fprintln(fc, "}")
+       }
+
        fgo2.Close()
        fc.Close()
 }
@@ -264,6 +281,7 @@ func (p *Package) structType(n *Name) (string, int64) {
 func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
        name := n.Go
        gtype := n.FuncType.Go
+       void := gtype.Results == nil || len(gtype.Results.List) == 0
        if n.AddError {
                // Add "error" to return type list.
                // Type list is known to be 0 or 1 element - it's a C function.
@@ -288,38 +306,58 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
 
        if *gccgo {
                // Gccgo style hooks.
-               // we hook directly into C. gccgo goes not support cgocall yet.
-               if !n.AddError {
-                       fmt.Fprintf(fgo2, "//extern %s\n", n.C)
-                       conf.Fprint(fgo2, fset, d)
-                       fmt.Fprint(fgo2, "\n")
-               } else {
-                       // write a small wrapper to retrieve errno.
-                       cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle)
-                       paramnames := []string(nil)
-                       for i, param := range d.Type.Params.List {
-                               paramName := fmt.Sprintf("p%d", i)
-                               param.Names = []*ast.Ident{ast.NewIdent(paramName)}
-                               paramnames = append(paramnames, paramName)
+               fmt.Fprint(fgo2, "\n")
+               cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle)
+               paramnames := []string(nil)
+               for i, param := range d.Type.Params.List {
+                       paramName := fmt.Sprintf("p%d", i)
+                       param.Names = []*ast.Ident{ast.NewIdent(paramName)}
+                       paramnames = append(paramnames, paramName)
+               }
+
+               conf.Fprint(fgo2, fset, d)
+               fmt.Fprint(fgo2, " {\n")
+               fmt.Fprint(fgo2, "\tdefer syscall.CgocallDone()\n")
+               fmt.Fprint(fgo2, "\tsyscall.Cgocall()\n")
+               if n.AddError {
+                       fmt.Fprint(fgo2, "\tsyscall.SetErrno(0)\n")
+               }
+               fmt.Fprint(fgo2, "\t")
+               if !void {
+                       fmt.Fprint(fgo2, "r := ")
+               }
+               fmt.Fprintf(fgo2, "%s(%s)\n", cname, strings.Join(paramnames, ", "))
+
+               if n.AddError {
+                       fmt.Fprint(fgo2, "\te := syscall.GetErrno()\n")
+                       fmt.Fprint(fgo2, "\tif e != 0 {\n")
+                       fmt.Fprint(fgo2, "\t\treturn ")
+                       if !void {
+                               fmt.Fprint(fgo2, "r, ")
+                       }
+                       fmt.Fprint(fgo2, "e\n")
+                       fmt.Fprint(fgo2, "\t}\n")
+                       fmt.Fprint(fgo2, "\treturn ")
+                       if !void {
+                               fmt.Fprint(fgo2, "r, ")
                        }
-                       conf.Fprint(fgo2, fset, d)
-                       fmt.Fprintf(fgo2, "{\n")
-                       fmt.Fprintf(fgo2, "\tsyscall.SetErrno(0)\n")
-                       fmt.Fprintf(fgo2, "\tr := %s(%s)\n", cname, strings.Join(paramnames, ", "))
-                       fmt.Fprintf(fgo2, "\te := syscall.GetErrno()\n")
-                       fmt.Fprintf(fgo2, "\tif e != 0 {\n")
-                       fmt.Fprintf(fgo2, "\t\treturn r, e\n")
-                       fmt.Fprintf(fgo2, "\t}\n")
-                       fmt.Fprintf(fgo2, "\treturn r, nil\n")
-                       fmt.Fprintf(fgo2, "}\n")
-                       // declare the C function.
-                       fmt.Fprintf(fgo2, "//extern %s\n", n.C)
-                       d.Name = ast.NewIdent(cname)
+                       fmt.Fprint(fgo2, "nil\n")
+               } else if !void {
+                       fmt.Fprint(fgo2, "\treturn r\n")
+               }
+
+               fmt.Fprint(fgo2, "}\n")
+
+               // declare the C function.
+               fmt.Fprintf(fgo2, "//extern %s\n", n.C)
+               d.Name = ast.NewIdent(cname)
+               if n.AddError {
                        l := d.Type.Results.List
                        d.Type.Results.List = l[:len(l)-1]
-                       conf.Fprint(fgo2, fset, d)
-                       fmt.Fprint(fgo2, "\n")
                }
+               conf.Fprint(fgo2, fset, d)
+               fmt.Fprint(fgo2, "\n")
+
                return
        }
        conf.Fprint(fgo2, fset, d)
@@ -660,46 +698,22 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) {
 func (p *Package) writeGccgoExports(fgo2, fc, fm *os.File) {
        fgcc := creat(*objDir + "_cgo_export.c")
        fgcch := creat(*objDir + "_cgo_export.h")
-       _ = fgcc
+
+       gccgoSymbolPrefix := p.gccgoSymbolPrefix()
 
        fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n")
        fmt.Fprintf(fgcch, "%s\n", p.Preamble)
        fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog())
-       fmt.Fprintf(fm, "#include \"_cgo_export.h\"\n")
 
-       clean := func(r rune) rune {
-               switch {
-               case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z',
-                       '0' <= r && r <= '9':
-                       return r
-               }
-               return '_'
-       }
+       fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n")
+       fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n")
 
-       var gccgoSymbolPrefix string
-       if *gccgopkgpath != "" {
-               gccgoSymbolPrefix = strings.Map(clean, *gccgopkgpath)
-       } else {
-               if *gccgoprefix == "" && p.PackageName == "main" {
-                       gccgoSymbolPrefix = "main"
-               } else {
-                       prefix := strings.Map(clean, *gccgoprefix)
-                       if prefix == "" {
-                               prefix = "go"
-                       }
-                       gccgoSymbolPrefix = prefix + "." + p.PackageName
-               }
-       }
+       fmt.Fprintf(fm, "#include \"_cgo_export.h\"\n")
 
        for _, exp := range p.ExpFunc {
-               // TODO: support functions with receivers.
                fn := exp.Func
                fntype := fn.Type
 
-               if !ast.IsExported(fn.Name.Name) {
-                       fatalf("cannot export unexported function %s with gccgo", fn.Name)
-               }
-
                cdeclBuf := new(bytes.Buffer)
                resultCount := 0
                forFieldList(fntype.Results,
@@ -725,28 +739,135 @@ func (p *Package) writeGccgoExports(fgo2, fc, fm *os.File) {
                        fmt.Fprintf(cdeclBuf, "struct %s_result", exp.ExpName)
                }
 
-               // The function name.
-               fmt.Fprintf(cdeclBuf, " "+exp.ExpName)
-               gccgoSymbol := fmt.Sprintf("%s.%s", gccgoSymbolPrefix, exp.Func.Name)
-               fmt.Fprintf(cdeclBuf, " (")
+               cRet := cdeclBuf.String()
+
+               cdeclBuf = new(bytes.Buffer)
+               fmt.Fprintf(cdeclBuf, "(")
+               if fn.Recv != nil {
+                       fmt.Fprintf(cdeclBuf, "%s recv", p.cgoType(fn.Recv.List[0].Type).C.String())
+               }
                // Function parameters.
                forFieldList(fntype.Params,
                        func(i int, atype ast.Expr) {
-                               if i > 0 {
+                               if i > 0 || fn.Recv != nil {
                                        fmt.Fprintf(cdeclBuf, ", ")
                                }
                                t := p.cgoType(atype)
                                fmt.Fprintf(cdeclBuf, "%s p%d", t.C, i)
                        })
                fmt.Fprintf(cdeclBuf, ")")
-               cdecl := cdeclBuf.String()
+               cParams := cdeclBuf.String()
+
+               goName := "Cgoexp_" + exp.ExpName
+               fmt.Fprintf(fgcch, `extern %s %s %s __asm__("%s.%s");`, cRet, goName, cParams, gccgoSymbolPrefix, goName)
+               fmt.Fprint(fgcch, "\n")
+
+               fmt.Fprint(fgcc, "\n")
+               fmt.Fprintf(fgcc, "%s %s %s {\n", cRet, exp.ExpName, cParams)
+               fmt.Fprint(fgcc, "\t")
+               if resultCount > 0 {
+                       fmt.Fprint(fgcc, "return ")
+               }
+               fmt.Fprintf(fgcc, "%s(", goName)
+               if fn.Recv != nil {
+                       fmt.Fprint(fgcc, "recv")
+               }
+               forFieldList(fntype.Params,
+                       func(i int, atype ast.Expr) {
+                               if i > 0 || fn.Recv != nil {
+                                       fmt.Fprintf(fgcc, ", ")
+                               }
+                               fmt.Fprintf(fgcc, "p%d", i)
+                       })
+               fmt.Fprint(fgcc, ");\n")
+               fmt.Fprint(fgcc, "}\n")
 
-               fmt.Fprintf(fgcch, "extern %s __asm__(\"%s\");\n", cdecl, gccgoSymbol)
                // Dummy declaration for _cgo_main.c
-               fmt.Fprintf(fm, "%s {}\n", cdecl)
+               fmt.Fprintf(fm, "%s %s %s {}\n", cRet, goName, cParams)
+
+               // For gccgo we use a wrapper function in Go, in order
+               // to call CgocallBack and CgocallBackDone.
+
+               // This code uses printer.Fprint, not conf.Fprint,
+               // because we don't want //line comments in the middle
+               // of the function types.
+               fmt.Fprint(fgo2, "\n")
+               fmt.Fprintf(fgo2, "func %s(", goName)
+               if fn.Recv != nil {
+                       fmt.Fprint(fgo2, "recv ")
+                       printer.Fprint(fgo2, fset, fn.Recv.List[0].Type)
+               }
+               forFieldList(fntype.Params,
+                       func(i int, atype ast.Expr) {
+                               if i > 0 || fn.Recv != nil {
+                                       fmt.Fprintf(fgo2, ", ")
+                               }
+                               fmt.Fprintf(fgo2, "p%d ", i)
+                               printer.Fprint(fgo2, fset, atype)
+                       })
+               fmt.Fprintf(fgo2, ")")
+               if resultCount > 0 {
+                       fmt.Fprintf(fgo2, " (")
+                       forFieldList(fntype.Results,
+                               func(i int, atype ast.Expr) {
+                                       if i > 0 {
+                                               fmt.Fprint(fgo2, ", ")
+                                       }
+                                       printer.Fprint(fgo2, fset, atype)
+                               })
+                       fmt.Fprint(fgo2, ")")
+               }
+               fmt.Fprint(fgo2, " {\n")
+               fmt.Fprint(fgo2, "\tsyscall.CgocallBack()\n")
+               fmt.Fprint(fgo2, "\tdefer syscall.CgocallBackDone()\n")
+               fmt.Fprint(fgo2, "\t")
+               if resultCount > 0 {
+                       fmt.Fprint(fgo2, "return ")
+               }
+               if fn.Recv != nil {
+                       fmt.Fprint(fgo2, "recv.")
+               }
+               fmt.Fprintf(fgo2, "%s(", exp.Func.Name)
+               forFieldList(fntype.Params,
+                       func(i int, atype ast.Expr) {
+                               if i > 0 {
+                                       fmt.Fprint(fgo2, ", ")
+                               }
+                               fmt.Fprintf(fgo2, "p%d", i)
+                       })
+               fmt.Fprint(fgo2, ")\n")
+               fmt.Fprint(fgo2, "}\n")
        }
 }
 
+// Return the package prefix when using gccgo.
+func (p *Package) gccgoSymbolPrefix() string {
+       if !*gccgo {
+               return ""
+       }
+
+       clean := func(r rune) rune {
+               switch {
+               case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z',
+                       '0' <= r && r <= '9':
+                       return r
+               }
+               return '_'
+       }
+
+       if *gccgopkgpath != "" {
+               return strings.Map(clean, *gccgopkgpath)
+       }
+       if *gccgoprefix == "" && p.PackageName == "main" {
+               return "main"
+       }
+       prefix := strings.Map(clean, *gccgoprefix)
+       if prefix == "" {
+               prefix = "go"
+       }
+       return prefix + "." + p.PackageName
+}
+
 // Call a function for each entry in an ast.FieldList, passing the
 // index into the list and the type.
 func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) {
@@ -940,6 +1061,8 @@ const cPrologGccgo = `
 #include <stdint.h>
 #include <string.h>
 
+typedef unsigned char byte;
+
 struct __go_string {
        const unsigned char *__data;
        int __length;