1
0
mirror of https://github.com/golang/go synced 2024-11-20 01:14:40 -07:00

cmd/cgo: improve gccgo support

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
This commit is contained in:
Ian Lance Taylor 2012-11-01 11:21:30 -07:00
parent f284a3ff4d
commit 3b04d23cbf
2 changed files with 198 additions and 68 deletions

View 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
}
}

View 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, ")
}
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, "e\n")
fmt.Fprint(fgo2, "\t}\n")
fmt.Fprint(fgo2, "\treturn ")
if !void {
fmt.Fprint(fgo2, "r, ")
}
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(fgcc, "/* Created by cgo - DO NOT EDIT. */\n")
fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n")
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 '_'
}
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
}
}
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;