Commit 54138e1a authored by Russ Cox's avatar Russ Cox

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
parent 012ceed9
...@@ -13,12 +13,13 @@ void callPanic(void); ...@@ -13,12 +13,13 @@ void callPanic(void);
import "C" import "C"
import ( import (
"./backdoor"
"path" "path"
"runtime" "runtime"
"strings" "strings"
"testing" "testing"
"unsafe" "unsafe"
"./backdoor"
) )
// nestedCall calls into C, back into Go, and finally to f. // nestedCall calls into C, back into Go, and finally to f.
...@@ -155,8 +156,8 @@ func testCallbackCallers(t *testing.T) { ...@@ -155,8 +156,8 @@ func testCallbackCallers(t *testing.T) {
"runtime.cgocallbackg1", "runtime.cgocallbackg1",
"runtime.cgocallbackg", "runtime.cgocallbackg",
"runtime.cgocallback_gofunc", "runtime.cgocallback_gofunc",
"runtime.asmcgocall", "runtime.asmcgocall_errno",
"runtime.cgocall", "runtime.cgocall_errno",
"test._Cfunc_callback", "test._Cfunc_callback",
"test.nestedCall", "test.nestedCall",
"test.testCallbackCallers", "test.testCallbackCallers",
......
...@@ -58,16 +58,14 @@ func (p *Package) writeDefs() { ...@@ -58,16 +58,14 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n") fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n")
fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName) fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName)
fmt.Fprintf(fgo2, "import \"unsafe\"\n\n") fmt.Fprintf(fgo2, "import \"unsafe\"\n\n")
if *importSyscall {
fmt.Fprintf(fgo2, "import \"syscall\"\n\n")
}
if !*gccgo && *importRuntimeCgo { if !*gccgo && *importRuntimeCgo {
fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n") 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 { 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)) typedefNames := make([]string, 0, len(typedef))
for name := range typedef { for name := range typedef {
...@@ -87,9 +85,10 @@ func (p *Package) writeDefs() { ...@@ -87,9 +85,10 @@ func (p *Package) writeDefs() {
} }
if *gccgo { if *gccgo {
fmt.Fprintf(fc, p.cPrologGccgo()) fmt.Fprint(fc, p.cPrologGccgo())
} else { } else {
fmt.Fprintf(fc, cProlog) fmt.Fprint(fc, cProlog)
fmt.Fprint(fgo2, goProlog)
} }
gccgoSymbolPrefix := p.gccgoSymbolPrefix() gccgoSymbolPrefix := p.gccgoSymbolPrefix()
...@@ -296,10 +295,6 @@ func (p *Package) structType(n *Name) (string, int64) { ...@@ -296,10 +295,6 @@ func (p *Package) structType(n *Name) (string, int64) {
fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad)
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 { if off == 0 {
fmt.Fprintf(&buf, "\t\tchar unused;\n") // avoid empty struct fmt.Fprintf(&buf, "\t\tchar unused;\n") // avoid empty struct
} }
...@@ -334,19 +329,18 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { ...@@ -334,19 +329,18 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
} }
// Builtins defined in the C prolog. // 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 { if *gccgo {
// Gccgo style hooks. // Gccgo style hooks.
fmt.Fprint(fgo2, "\n") 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) conf.Fprint(fgo2, fset, d)
fmt.Fprint(fgo2, " {\n") fmt.Fprint(fgo2, " {\n")
if !inProlog { if !inProlog {
...@@ -383,7 +377,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { ...@@ -383,7 +377,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
fmt.Fprint(fgo2, "}\n") fmt.Fprint(fgo2, "}\n")
// declare the C function. // 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) d.Name = ast.NewIdent(cname)
if n.AddError { if n.AddError {
l := d.Type.Results.List l := d.Type.Results.List
...@@ -394,61 +388,49 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { ...@@ -394,61 +388,49 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
return return
} }
conf.Fprint(fgo2, fset, d)
fmt.Fprint(fgo2, "\n")
if inProlog { if inProlog {
fmt.Fprint(fgo2, builtinDefs[name])
return return
} }
var argSize int64
_, argSize = p.structType(n)
// C wrapper calls into gcc, passing a pointer to the argument frame. // 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, "#pragma cgo_import_static %s\n", cname)
fmt.Fprintf(fc, "void _cgo%s%s(void*);\n", cPrefix, n.Mangle) fmt.Fprintf(fc, "void %s(void*);\n", cname)
fmt.Fprintf(fc, "\n") fmt.Fprintf(fc, "void *·%s = %s;\n", cname, cname)
fmt.Fprintf(fc, "void\n")
if argSize == 0 { nret := 0
argSize++ 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 if n.AddError {
// there are pointers in the actual argument frame. d.Type.Results.List[nret].Names = []*ast.Ident{ast.NewIdent("r2")}
// 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 := 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") prefix := ""
fmt.Fprintf(fc, "\truntime·cgocall(_cgo%s%s, &p);\n", cPrefix, n.Mangle)
if n.AddError { if n.AddError {
// gcc leaves errno in first word of interface at end of p. prefix = "errno := "
// 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 */
}
}`)
} }
fmt.Fprintf(fc, "}\n") fmt.Fprintf(fgo2, "\t%s_cgo_runtime_cgocall_errno(%s, %s)\n", prefix, cname, arg)
fmt.Fprintf(fc, "\n") 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 // 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) { ...@@ -521,7 +503,11 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
// Gcc wrapper unpacks the C argument struct // Gcc wrapper unpacks the C argument struct
// and calls the actual C function. // 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, "_cgo%s%s(void *v)\n", cPrefix, n.Mangle)
fmt.Fprintf(fgcc, "{\n") fmt.Fprintf(fgcc, "{\n")
if n.AddError { if n.AddError {
...@@ -557,7 +543,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { ...@@ -557,7 +543,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
} }
fmt.Fprintf(fgcc, ");\n") fmt.Fprintf(fgcc, ");\n")
if n.AddError { 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")
fmt.Fprintf(fgcc, "\n") fmt.Fprintf(fgcc, "\n")
...@@ -1166,46 +1152,74 @@ const cProlog = ` ...@@ -1166,46 +1152,74 @@ const cProlog = `
#include "runtime.h" #include "runtime.h"
#include "cgocall.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 ·_Cerrno(void*, int32);
`
void const goProlog = `
·_Cfunc_GoString(int8 *p, String s) var _cgo_runtime_cgocall_errno func(unsafe.Pointer, uintptr) int32
{ var _cgo_runtime_cmalloc func(uintptr) unsafe.Pointer
s = runtime·gostring((byte*)p); `
FLUSH(&s);
const goStringDef = `
var _cgo_runtime_gostring func(*_Ctype_char) string
func _Cfunc_GoString(p *_Ctype_char) string {
return _cgo_runtime_gostring(p)
} }
`
void const goStringNDef = `
·_Cfunc_GoStringN(int8 *p, int32 l, String s) var _cgo_runtime_gostringn func(*_Ctype_char, int) string
{ func _Cfunc_GoStringN(p *_Ctype_char, l _Ctype_int) string {
s = runtime·gostringn((byte*)p, l); return _cgo_runtime_gostringn(p, int(l))
FLUSH(&s);
} }
`
void const goBytesDef = `
·_Cfunc_GoBytes(int8 *p, int32 l, Slice s) var _cgo_runtime_gobytes func(unsafe.Pointer, int) []byte
{ func _Cfunc_GoBytes(p unsafe.Pointer, l _Ctype_int) []byte {
s = runtime·gobytes((byte*)p, l); return _cgo_runtime_gobytes(p, int(l))
FLUSH(&s);
} }
`
void const cStringDef = `
·_Cfunc_CString(String s, int8 *p) func _Cfunc_CString(s string) *_Ctype_char {
{ p := _cgo_runtime_cmalloc(uintptr(len(s)+1))
p = runtime·cmalloc(s.len+1); pp := (*[1<<30]byte)(p)
runtime·memmove((byte*)p, s.str, s.len); copy(pp[:], s)
p[s.len] = 0; pp[len(s)] = 0
FLUSH(&p); return (*_Ctype_char)(p)
} }
`
void const cMallocDef = `
·_Cfunc__CMalloc(uintptr n, int8 *p) func _Cfunc__CMalloc(n _Ctype_size_t) unsafe.Pointer {
{ return _cgo_runtime_cmalloc(uintptr(n))
p = runtime·cmalloc(n);
FLUSH(&p);
} }
` `
var builtinDefs = map[string]string{
"GoString": goStringDef,
"GoStringN": goStringNDef,
"GoBytes": goBytesDef,
"CString": cStringDef,
"_CMalloc": cMallocDef,
}
func (p *Package) cPrologGccgo() string { func (p *Package) cPrologGccgo() string {
return strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1) return strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1)
} }
......
...@@ -680,7 +680,15 @@ TEXT gosave<>(SB),NOSPLIT,$0 ...@@ -680,7 +680,15 @@ TEXT gosave<>(SB),NOSPLIT,$0
// Call fn(arg) on the scheduler stack, // Call fn(arg) on the scheduler stack,
// aligned appropriately for the gcc ABI. // aligned appropriately for the gcc ABI.
// See cgocall.c for more details. // 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 fn+0(FP), AX
MOVL arg+4(FP), BX MOVL arg+4(FP), BX
MOVL SP, DX MOVL SP, DX
...@@ -712,6 +720,7 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8 ...@@ -712,6 +720,7 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
MOVL 8(SP), DI MOVL 8(SP), DI
MOVL DI, g(CX) MOVL DI, g(CX)
MOVL 4(SP), SP MOVL 4(SP), SP
MOVL AX, ret+8(FP)
RET RET
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize) // cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
......
...@@ -764,7 +764,15 @@ TEXT gosave<>(SB),NOSPLIT,$0 ...@@ -764,7 +764,15 @@ TEXT gosave<>(SB),NOSPLIT,$0
// Call fn(arg) on the scheduler stack, // Call fn(arg) on the scheduler stack,
// aligned appropriately for the gcc ABI. // aligned appropriately for the gcc ABI.
// See cgocall.c for more details. // 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 fn+0(FP), AX
MOVQ arg+8(FP), BX MOVQ arg+8(FP), BX
MOVQ SP, DX MOVQ SP, DX
...@@ -805,6 +813,7 @@ nosave: ...@@ -805,6 +813,7 @@ nosave:
MOVQ 48(SP), DI MOVQ 48(SP), DI
MOVQ DI, g(CX) MOVQ DI, g(CX)
MOVQ 40(SP), SP MOVQ 40(SP), SP
MOVL AX, ret+16(FP)
RET RET
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize) // cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
......
...@@ -711,6 +711,12 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8 ...@@ -711,6 +711,12 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
MOVL 0, AX MOVL 0, AX
RET 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) // cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
// Not implemented. // Not implemented.
TEXT runtime·cgocallback(SB),NOSPLIT,$0-12 TEXT runtime·cgocallback(SB),NOSPLIT,$0-12
......
...@@ -493,7 +493,15 @@ TEXT gosave<>(SB),NOSPLIT,$0 ...@@ -493,7 +493,15 @@ TEXT gosave<>(SB),NOSPLIT,$0
// Call fn(arg) on the scheduler stack, // Call fn(arg) on the scheduler stack,
// aligned appropriately for the gcc ABI. // aligned appropriately for the gcc ABI.
// See cgocall.c for more details. // 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 fn+0(FP), R1
MOVW arg+4(FP), R0 MOVW arg+4(FP), R0
MOVW R13, R2 MOVW R13, R2
...@@ -521,6 +529,7 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8 ...@@ -521,6 +529,7 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
// Restore registers, g, stack pointer. // Restore registers, g, stack pointer.
MOVW 20(R13), g MOVW 20(R13), g
MOVW 16(R13), R13 MOVW 16(R13), R13
MOVW R0, ret+8(FP)
RET RET
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize) // cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
......
...@@ -96,8 +96,15 @@ static FuncVal endcgoV = { endcgo }; ...@@ -96,8 +96,15 @@ static FuncVal endcgoV = { endcgo };
void void
runtime·cgocall(void (*fn)(void*), void *arg) runtime·cgocall(void (*fn)(void*), void *arg)
{
runtime·cgocall_errno(fn, arg);
}
int32
runtime·cgocall_errno(void (*fn)(void*), void *arg)
{ {
Defer d; Defer d;
int32 errno;
if(!runtime·iscgo && !Solaris && !Windows) if(!runtime·iscgo && !Solaris && !Windows)
runtime·throw("cgocall unavailable"); runtime·throw("cgocall unavailable");
...@@ -140,13 +147,15 @@ runtime·cgocall(void (*fn)(void*), void *arg) ...@@ -140,13 +147,15 @@ runtime·cgocall(void (*fn)(void*), void *arg)
* the $GOMAXPROCS accounting. * the $GOMAXPROCS accounting.
*/ */
runtime·entersyscall(); runtime·entersyscall();
runtime·asmcgocall(fn, arg); errno = runtime·asmcgocall_errno(fn, arg);
runtime·exitsyscall(); runtime·exitsyscall();
if(g->defer != &d || d.fn != &endcgoV) if(g->defer != &d || d.fn != &endcgoV)
runtime·throw("runtime: bad defer entry in cgocallback"); runtime·throw("runtime: bad defer entry in cgocallback");
g->defer = d.link; g->defer = d.link;
endcgo(); endcgo();
return errno;
} }
static void static void
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
*/ */
void runtime·cgocall(void (*fn)(void*), void*); 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·cgocallback(void (*fn)(void), void*, uintptr);
void *runtime·cmalloc(uintptr); void *runtime·cmalloc(uintptr);
void runtime·cfree(void*); void runtime·cfree(void*);
...@@ -848,6 +848,7 @@ void runtime·tsleep(int64, String); ...@@ -848,6 +848,7 @@ void runtime·tsleep(int64, String);
M* runtime·newm(void); M* runtime·newm(void);
void runtime·goexit(void); void runtime·goexit(void);
void runtime·asmcgocall(void (*fn)(void*), void*); void runtime·asmcgocall(void (*fn)(void*), void*);
int32 runtime·asmcgocall_errno(void (*fn)(void*), void*);
void runtime·entersyscall(void); void runtime·entersyscall(void);
void runtime·entersyscallblock(void); void runtime·entersyscallblock(void);
void runtime·exitsyscall(void); void runtime·exitsyscall(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment