]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/cgo, runtime: write cgo stub wrappers in Go, not C
authorRuss Cox <rsc@golang.org>
Wed, 3 Sep 2014 15:36:14 +0000 (11:36 -0400)
committerRuss Cox <rsc@golang.org>
Wed, 3 Sep 2014 15:36:14 +0000 (11:36 -0400)
LGTM=alex.brainman, iant
R=golang-codereviews, alex.brainman, iant
CC=dvyukov, golang-codereviews, khr, r
https://golang.org/cl/139070043

misc/cgo/test/callback.go
src/cmd/cgo/out.go
src/pkg/runtime/asm_386.s
src/pkg/runtime/asm_amd64.s
src/pkg/runtime/asm_amd64p32.s
src/pkg/runtime/asm_arm.s
src/pkg/runtime/cgocall.c
src/pkg/runtime/cgocall.h
src/pkg/runtime/runtime.h

index 82ed015bd807b5698feca83d0b74044f7975a879..67d271404a31fd464f703d1b9462f700cb53e369 100644 (file)
@@ -13,12 +13,13 @@ void callPanic(void);
 import "C"
 
 import (
-       "./backdoor"
        "path"
        "runtime"
        "strings"
        "testing"
        "unsafe"
+
+       "./backdoor"
 )
 
 // nestedCall calls into C, back into Go, and finally to f.
@@ -155,8 +156,8 @@ func testCallbackCallers(t *testing.T) {
                "runtime.cgocallbackg1",
                "runtime.cgocallbackg",
                "runtime.cgocallback_gofunc",
-               "runtime.asmcgocall",
-               "runtime.cgocall",
+               "runtime.asmcgocall_errno",
+               "runtime.cgocall_errno",
                "test._Cfunc_callback",
                "test.nestedCall",
                "test.testCallbackCallers",
index 6322e0604a4aadf654df2e629c30d5277de07b36..6586531ada3b6e0eea4ddb431e99e186f48b72e7 100644 (file)
@@ -58,16 +58,14 @@ func (p *Package) writeDefs() {
        fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n")
        fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName)
        fmt.Fprintf(fgo2, "import \"unsafe\"\n\n")
-       if *importSyscall {
-               fmt.Fprintf(fgo2, "import \"syscall\"\n\n")
-       }
        if !*gccgo && *importRuntimeCgo {
                fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n")
        }
-       fmt.Fprintf(fgo2, "func _Cgo_ptr(ptr unsafe.Pointer) unsafe.Pointer { return ptr }\n\n")
        if *importSyscall {
-               fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int32) { *dst = syscall.Errno(x) }\n")
+               fmt.Fprintf(fgo2, "import \"syscall\"\n\n")
+               fmt.Fprintf(fgo2, "var _ syscall.Errno\n")
        }
+       fmt.Fprintf(fgo2, "func _Cgo_ptr(ptr unsafe.Pointer) unsafe.Pointer { return ptr }\n\n")
 
        typedefNames := make([]string, 0, len(typedef))
        for name := range typedef {
@@ -87,9 +85,10 @@ func (p *Package) writeDefs() {
        }
 
        if *gccgo {
-               fmt.Fprintf(fc, p.cPrologGccgo())
+               fmt.Fprint(fc, p.cPrologGccgo())
        } else {
-               fmt.Fprintf(fc, cProlog)
+               fmt.Fprint(fc, cProlog)
+               fmt.Fprint(fgo2, goProlog)
        }
 
        gccgoSymbolPrefix := p.gccgoSymbolPrefix()
@@ -296,10 +295,6 @@ func (p *Package) structType(n *Name) (string, int64) {
                fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad)
                off += pad
        }
-       if n.AddError {
-               fmt.Fprint(&buf, "\t\tint e[2*sizeof(void *)/sizeof(int)]; /* error */\n")
-               off += 2 * p.PtrSize
-       }
        if off == 0 {
                fmt.Fprintf(&buf, "\t\tchar unused;\n") // avoid empty struct
        }
@@ -334,19 +329,18 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
        }
 
        // Builtins defined in the C prolog.
-       inProlog := name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes" || name == "_CMalloc"
+       inProlog := builtinDefs[name] != ""
+       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)
+       }
 
        if *gccgo {
                // Gccgo style hooks.
                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")
                if !inProlog {
@@ -383,7 +377,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
                fmt.Fprint(fgo2, "}\n")
 
                // declare the C function.
-               fmt.Fprintf(fgo2, "//extern _cgo%s%s\n", cPrefix, n.Mangle)
+               fmt.Fprintf(fgo2, "//extern %s\n", cname)
                d.Name = ast.NewIdent(cname)
                if n.AddError {
                        l := d.Type.Results.List
@@ -394,61 +388,49 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
 
                return
        }
-       conf.Fprint(fgo2, fset, d)
-       fmt.Fprint(fgo2, "\n")
 
        if inProlog {
+               fmt.Fprint(fgo2, builtinDefs[name])
                return
        }
 
-       var argSize int64
-       _, argSize = p.structType(n)
-
        // C wrapper calls into gcc, passing a pointer to the argument frame.
-       fmt.Fprintf(fc, "#pragma cgo_import_static _cgo%s%s\n", cPrefix, n.Mangle)
-       fmt.Fprintf(fc, "void _cgo%s%s(void*);\n", cPrefix, n.Mangle)
-       fmt.Fprintf(fc, "\n")
-       fmt.Fprintf(fc, "void\n")
-       if argSize == 0 {
-               argSize++
+       fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", cname)
+       fmt.Fprintf(fc, "void %s(void*);\n", cname)
+       fmt.Fprintf(fc, "void *·%s = %s;\n", cname, cname)
+
+       nret := 0
+       if !void {
+               d.Type.Results.List[0].Names = []*ast.Ident{ast.NewIdent("r1")}
+               nret = 1
        }
-       // TODO(rsc): The struct here should declare pointers only where
-       // there are pointers in the actual argument frame.
-       // This is a workaround for golang.org/issue/6397.
-       fmt.Fprintf(fc, "·%s(struct{", n.Mangle)
-       if n := argSize / p.PtrSize; n > 0 {
-               fmt.Fprintf(fc, "void *y[%d];", n)
+       if n.AddError {
+               d.Type.Results.List[nret].Names = []*ast.Ident{ast.NewIdent("r2")}
        }
-       if n := argSize % p.PtrSize; n > 0 {
-               fmt.Fprintf(fc, "uint8 x[%d];", n)
+
+       fmt.Fprint(fgo2, "\n")
+       fmt.Fprintf(fgo2, "var %s unsafe.Pointer\n", cname)
+       conf.Fprint(fgo2, fset, d)
+       fmt.Fprint(fgo2, " {\n")
+
+       // NOTE: Using uintptr to hide from escape analysis.
+       arg := "0"
+       if len(paramnames) > 0 {
+               arg = "uintptr(unsafe.Pointer(&p0))"
+       } else if !void {
+               arg = "uintptr(unsafe.Pointer(&r1))"
        }
-       fmt.Fprintf(fc, "}p)\n")
-       fmt.Fprintf(fc, "{\n")
-       fmt.Fprintf(fc, "\truntime·cgocall(_cgo%s%s, &p);\n", cPrefix, n.Mangle)
+
+       prefix := ""
        if n.AddError {
-               // gcc leaves errno in first word of interface at end of p.
-               // check whether it is zero; if so, turn interface into nil.
-               // if not, turn interface into errno.
-               // Go init function initializes ·_Cerrno with an os.Errno
-               // for us to copy.
-               fmt.Fprintln(fc, `      {
-                       int32 e;
-                       void **v;
-                       v = (void**)(&p+1) - 2; /* v = final two void* of p */
-                       e = *(int32*)v;
-                       v[0] = (void*)0xdeadbeef;
-                       v[1] = (void*)0xdeadbeef;
-                       if(e == 0) {
-                               /* nil interface */
-                               v[0] = 0;
-                               v[1] = 0;
-                       } else {
-                               ·_Cerrno(v, e);        /* fill in v as error for errno e */
-                       }
-               }`)
+               prefix = "errno := "
        }
-       fmt.Fprintf(fc, "}\n")
-       fmt.Fprintf(fc, "\n")
+       fmt.Fprintf(fgo2, "\t%s_cgo_runtime_cgocall_errno(%s, %s)\n", prefix, cname, arg)
+       if n.AddError {
+               fmt.Fprintf(fgo2, "\tif errno != 0 { r2 = syscall.Errno(errno) }\n")
+       }
+       fmt.Fprintf(fgo2, "\treturn\n")
+       fmt.Fprintf(fgo2, "}\n")
 }
 
 // writeOutput creates stubs for a specific source file to be compiled by 6g
@@ -521,7 +503,11 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
 
        // Gcc wrapper unpacks the C argument struct
        // and calls the actual C function.
-       fmt.Fprintf(fgcc, "void\n")
+       if n.AddError {
+               fmt.Fprintf(fgcc, "int\n")
+       } else {
+               fmt.Fprintf(fgcc, "void\n")
+       }
        fmt.Fprintf(fgcc, "_cgo%s%s(void *v)\n", cPrefix, n.Mangle)
        fmt.Fprintf(fgcc, "{\n")
        if n.AddError {
@@ -557,7 +543,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
        }
        fmt.Fprintf(fgcc, ");\n")
        if n.AddError {
-               fmt.Fprintf(fgcc, "\t*(int*)(a->e) = errno;\n")
+               fmt.Fprintf(fgcc, "\treturn errno;\n")
        }
        fmt.Fprintf(fgcc, "}\n")
        fmt.Fprintf(fgcc, "\n")
@@ -1166,46 +1152,74 @@ const cProlog = `
 #include "runtime.h"
 #include "cgocall.h"
 
+static void *cgocall_errno = runtime·cgocall_errno;
+void *·_cgo_runtime_cgocall_errno = &cgocall_errno;
+
+static void *runtime_gostring = runtime·gostring;
+void *·_cgo_runtime_gostring = &runtime_gostring;
+
+static void *runtime_gostringn = runtime·gostringn;
+void *·_cgo_runtime_gostringn = &runtime_gostringn;
+
+static void *runtime_gobytes = runtime·gobytes;
+void *·_cgo_runtime_gobytes = &runtime_gobytes;
+
+static void *runtime_cmalloc = runtime·cmalloc;
+void *·_cgo_runtime_cmalloc = &runtime_cmalloc;
+
 void ·_Cerrno(void*, int32);
+`
 
-void
-·_Cfunc_GoString(int8 *p, String s)
-{
-       s = runtime·gostring((byte*)p);
-       FLUSH(&s);
+const goProlog = `
+var _cgo_runtime_cgocall_errno func(unsafe.Pointer, uintptr) int32
+var _cgo_runtime_cmalloc func(uintptr) unsafe.Pointer
+`
+
+const goStringDef = `
+var _cgo_runtime_gostring func(*_Ctype_char) string
+func _Cfunc_GoString(p *_Ctype_char) string {
+       return _cgo_runtime_gostring(p)
 }
+`
 
-void
-·_Cfunc_GoStringN(int8 *p, int32 l, String s)
-{
-       s = runtime·gostringn((byte*)p, l);
-       FLUSH(&s);
+const goStringNDef = `
+var _cgo_runtime_gostringn func(*_Ctype_char, int) string
+func _Cfunc_GoStringN(p *_Ctype_char, l _Ctype_int) string {
+       return _cgo_runtime_gostringn(p, int(l))
 }
+`
 
-void
-·_Cfunc_GoBytes(int8 *p, int32 l, Slice s)
-{
-       s = runtime·gobytes((byte*)p, l);
-       FLUSH(&s);
+const goBytesDef = `
+var _cgo_runtime_gobytes func(unsafe.Pointer, int) []byte
+func _Cfunc_GoBytes(p unsafe.Pointer, l _Ctype_int) []byte {
+       return _cgo_runtime_gobytes(p, int(l))
 }
+`
 
-void
-·_Cfunc_CString(String s, int8 *p)
-{
-       p = runtime·cmalloc(s.len+1);
-       runtime·memmove((byte*)p, s.str, s.len);
-       p[s.len] = 0;
-       FLUSH(&p);
+const cStringDef = `
+func _Cfunc_CString(s string) *_Ctype_char {
+       p := _cgo_runtime_cmalloc(uintptr(len(s)+1))
+       pp := (*[1<<30]byte)(p)
+       copy(pp[:], s)
+       pp[len(s)] = 0
+       return (*_Ctype_char)(p)
 }
+`
 
-void
-·_Cfunc__CMalloc(uintptr n, int8 *p)
-{
-       p = runtime·cmalloc(n);
-       FLUSH(&p);
+const cMallocDef = `
+func _Cfunc__CMalloc(n _Ctype_size_t) unsafe.Pointer {
+       return _cgo_runtime_cmalloc(uintptr(n))
 }
 `
 
+var builtinDefs = map[string]string{
+       "GoString":  goStringDef,
+       "GoStringN": goStringNDef,
+       "GoBytes":   goBytesDef,
+       "CString":   cStringDef,
+       "_CMalloc":  cMallocDef,
+}
+
 func (p *Package) cPrologGccgo() string {
        return strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1)
 }
index 07158ef0fdbd01319110f167c4baa31149b3e037..8a5fd3fb0846754de378c878009ebff040af3333 100644 (file)
@@ -680,7 +680,15 @@ TEXT gosave<>(SB),NOSPLIT,$0
 // Call fn(arg) on the scheduler stack,
 // aligned appropriately for the gcc ABI.
 // See cgocall.c for more details.
-TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
+TEXT runtime·asmcgocall(SB),NOSPLIT,$12-8
+       MOVL    fn+0(FP), AX
+       MOVL    arg+4(FP), BX
+       MOVL    AX, 0(SP)
+       MOVL    BX, 4(SP)
+       CALL    runtime·asmcgocall_errno(SB)
+       RET
+
+TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-12
        MOVL    fn+0(FP), AX
        MOVL    arg+4(FP), BX
        MOVL    SP, DX
@@ -712,6 +720,7 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
        MOVL    8(SP), DI
        MOVL    DI, g(CX)
        MOVL    4(SP), SP
+       MOVL    AX, ret+8(FP)
        RET
 
 // cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
index 1d98fc26526ec26b0c63e532e1cdef3ab530d3cc..0846966f123fee93ee7906bb1829598bf2f8cf44 100644 (file)
@@ -764,7 +764,15 @@ TEXT gosave<>(SB),NOSPLIT,$0
 // Call fn(arg) on the scheduler stack,
 // aligned appropriately for the gcc ABI.
 // See cgocall.c for more details.
-TEXT runtime·asmcgocall(SB),NOSPLIT,$0-16
+TEXT runtime·asmcgocall(SB),NOSPLIT,$24-16
+       MOVQ    fn+0(FP), AX
+       MOVQ    arg+8(FP), BX
+       MOVQ    AX, 0(SP)
+       MOVQ    BX, 8(SP)
+       CALL    runtime·asmcgocall_errno(SB)
+       RET
+
+TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-20
        MOVQ    fn+0(FP), AX
        MOVQ    arg+8(FP), BX
        MOVQ    SP, DX
@@ -805,6 +813,7 @@ nosave:
        MOVQ    48(SP), DI
        MOVQ    DI, g(CX)
        MOVQ    40(SP), SP
+       MOVL    AX, ret+16(FP)
        RET
 
 // cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
index 20069a6c7ecc1305de343f65379a200b44497d29..7886861eb228b7a784af7ca987f2ac3f2cf8cbe8 100644 (file)
@@ -711,6 +711,12 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
        MOVL    0, AX
        RET
 
+// asmcgocall(void(*fn)(void*), void *arg)
+// Not implemented.
+TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-12
+       MOVL    0, AX
+       RET
+
 // cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
 // Not implemented.
 TEXT runtime·cgocallback(SB),NOSPLIT,$0-12
index 6e12cf60f356ebf36bfa93827bc51d07734758a2..67763e1b2e8e853791473e9541df61a60eab76c1 100644 (file)
@@ -493,7 +493,15 @@ TEXT gosave<>(SB),NOSPLIT,$0
 // Call fn(arg) on the scheduler stack,
 // aligned appropriately for the gcc ABI.
 // See cgocall.c for more details.
-TEXT   runtime·asmcgocall(SB),NOSPLIT,$0-8
+TEXT   runtime·asmcgocall(SB),NOSPLIT,$12-8
+       MOVW    fn+0(FP), R1
+       MOVW    arg+4(FP), R2
+       MOVW    R1, 0(R13)
+       MOVW    R2, 4(R13)
+       BL      runtime·asmcgocall_errno(SB)
+       RET
+
+TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-12
        MOVW    fn+0(FP), R1
        MOVW    arg+4(FP), R0
        MOVW    R13, R2
@@ -521,6 +529,7 @@ TEXT        runtime·asmcgocall(SB),NOSPLIT,$0-8
        // Restore registers, g, stack pointer.
        MOVW    20(R13), g
        MOVW    16(R13), R13
+       MOVW    R0, ret+8(FP)
        RET
 
 // cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
index 145ac7dc19fe9682a694b598a92038c3bfeeb2ff..0fd5fbd00cad15301ecef5840dcd0fc3cd3cdf4e 100644 (file)
@@ -96,8 +96,15 @@ static FuncVal endcgoV = { endcgo };
 
 void
 runtime·cgocall(void (*fn)(void*), void *arg)
+{
+       runtime·cgocall_errno(fn, arg);
+}
+
+int32
+runtime·cgocall_errno(void (*fn)(void*), void *arg)
 {
        Defer d;
+       int32 errno;
 
        if(!runtime·iscgo && !Solaris && !Windows)
                runtime·throw("cgocall unavailable");
@@ -140,13 +147,15 @@ runtime·cgocall(void (*fn)(void*), void *arg)
         * the $GOMAXPROCS accounting.
         */
        runtime·entersyscall();
-       runtime·asmcgocall(fn, arg);
+       errno = runtime·asmcgocall_errno(fn, arg);
        runtime·exitsyscall();
 
        if(g->defer != &d || d.fn != &endcgoV)
                runtime·throw("runtime: bad defer entry in cgocallback");
        g->defer = d.link;
        endcgo();
+       
+       return errno;
 }
 
 static void
index 253661a7e7364db91e5e24f68b5fae1c20ff319a..c87a9cdc5dd6d311882863d369a2b13dc67955c7 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 void runtime·cgocall(void (*fn)(void*), void*);
+int32 runtime·cgocall_errno(void (*fn)(void*), void*);
 void runtime·cgocallback(void (*fn)(void), void*, uintptr);
 void *runtime·cmalloc(uintptr);
 void runtime·cfree(void*);
index d67d7a0076de14e4c28baae69d211a635255a0fb..0d25ca6c51ec3a6acc636c586a1a45113f239964 100644 (file)
@@ -848,6 +848,7 @@ void        runtime·tsleep(int64, String);
 M*     runtime·newm(void);
 void   runtime·goexit(void);
 void   runtime·asmcgocall(void (*fn)(void*), void*);
+int32  runtime·asmcgocall_errno(void (*fn)(void*), void*);
 void   runtime·entersyscall(void);
 void   runtime·entersyscallblock(void);
 void   runtime·exitsyscall(void);