From 54138e1ac356d824fe669b593c9ec24d9c7bbd3a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 3 Sep 2014 11:36:14 -0400 Subject: [PATCH] cmd/cgo, runtime: write cgo stub wrappers in Go, not C 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 | 7 +- src/cmd/cgo/out.go | 202 ++++++++++++++++++--------------- src/pkg/runtime/asm_386.s | 11 +- src/pkg/runtime/asm_amd64.s | 11 +- src/pkg/runtime/asm_amd64p32.s | 6 + src/pkg/runtime/asm_arm.s | 11 +- src/pkg/runtime/cgocall.c | 11 +- src/pkg/runtime/cgocall.h | 1 + src/pkg/runtime/runtime.h | 1 + 9 files changed, 160 insertions(+), 101 deletions(-) diff --git a/misc/cgo/test/callback.go b/misc/cgo/test/callback.go index 82ed015bd8..67d271404a 100644 --- a/misc/cgo/test/callback.go +++ b/misc/cgo/test/callback.go @@ -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", diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 6322e0604a..6586531ada 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -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) } diff --git a/src/pkg/runtime/asm_386.s b/src/pkg/runtime/asm_386.s index 07158ef0fd..8a5fd3fb08 100644 --- a/src/pkg/runtime/asm_386.s +++ b/src/pkg/runtime/asm_386.s @@ -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) diff --git a/src/pkg/runtime/asm_amd64.s b/src/pkg/runtime/asm_amd64.s index 1d98fc2652..0846966f12 100644 --- a/src/pkg/runtime/asm_amd64.s +++ b/src/pkg/runtime/asm_amd64.s @@ -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) diff --git a/src/pkg/runtime/asm_amd64p32.s b/src/pkg/runtime/asm_amd64p32.s index 20069a6c7e..7886861eb2 100644 --- a/src/pkg/runtime/asm_amd64p32.s +++ b/src/pkg/runtime/asm_amd64p32.s @@ -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 diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s index 6e12cf60f3..67763e1b2e 100644 --- a/src/pkg/runtime/asm_arm.s +++ b/src/pkg/runtime/asm_arm.s @@ -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) diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c index 145ac7dc19..0fd5fbd00c 100644 --- a/src/pkg/runtime/cgocall.c +++ b/src/pkg/runtime/cgocall.c @@ -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 diff --git a/src/pkg/runtime/cgocall.h b/src/pkg/runtime/cgocall.h index 253661a7e7..c87a9cdc5d 100644 --- a/src/pkg/runtime/cgocall.h +++ b/src/pkg/runtime/cgocall.h @@ -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*); diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index d67d7a0076..0d25ca6c51 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -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); -- 2.48.1