aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/asm_amd64.s
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2020-10-01 17:22:38 -0400
committerAustin Clements <austin@google.com>2020-10-26 14:50:32 +0000
commit30c18878730434027dbefd343aad74963a1fdc48 (patch)
treefa134fee222fee13eca873820988d42cafd33b25 /src/runtime/asm_amd64.s
parent404899f6b56800c1d8e0521fc9ce0c856e459d94 (diff)
downloadgo-30c18878730434027dbefd343aad74963a1fdc48.tar.gz
go-30c18878730434027dbefd343aad74963a1fdc48.zip
runtime,cmd/cgo: simplify C -> Go call path
This redesigns the way calls work from C to exported Go functions. It removes several steps from the call path, makes cmd/cgo no longer sensitive to the Go calling convention, and eliminates the use of reflectcall from cgo. In order to avoid generating a large amount of FFI glue between the C and Go ABIs, the cgo tool has long depended on generating a C function that marshals the arguments into a struct, and then the actual ABI switch happens in functions with fixed signatures that simply take a pointer to this struct. In a way, this CL simply pushes this idea further. Currently, the cgo tool generates this argument struct in the exact layout of the Go stack frame and depends on reflectcall to unpack it into the appropriate Go call (even though it's actually reflectcall'ing a function generated by cgo). In this CL, we decouple this struct from the Go stack layout. Instead, cgo generates a Go function that takes the struct, unpacks it, and calls the exported function. Since this generated function has a generic signature (like the rest of the call path), we don't need reflectcall and can instead depend on the Go compiler itself to implement the call to the exported Go function. One complication is that syscall.NewCallback on Windows, which converts a Go function into a C function pointer, depends on cgocallback's current dynamic calling approach since the signatures of the callbacks aren't known statically. For this specific case, we continue to depend on reflectcall. Really, the current approach makes some overly simplistic assumptions about translating the C ABI to the Go ABI. Now we're at least in a much better position to do a proper ABI translation. For comparison, the current cgo call path looks like: GoF (generated C function) -> crosscall2 (in cgo/asm_*.s) -> _cgoexp_GoF (generated Go function) -> cgocallback (in asm_*.s) -> cgocallback_gofunc (in asm_*.s) -> cgocallbackg (in cgocall.go) -> cgocallbackg1 (in cgocall.go) -> reflectcall (in asm_*.s) -> _cgoexpwrap_GoF (generated Go function) -> p.GoF Now the call path looks like: GoF (generated C function) -> crosscall2 (in cgo/asm_*.s) -> cgocallback (in asm_*.s) -> cgocallbackg (in cgocall.go) -> cgocallbackg1 (in cgocall.go) -> _cgoexp_GoF (generated Go function) -> p.GoF Notably: 1. We combine _cgoexp_GoF and _cgoexpwrap_GoF and move the combined operation to the end of the sequence. This combined function also handles reflectcall's previous role. 2. We combined cgocallback and cgocallback_gofunc since the only purpose of having both was to convert a raw PC into a Go function value. We instead construct the Go function value in cgocallbackg1. 3. cgocallbackg1 no longer reaches backwards through the stack to get the arguments to cgocallback_gofunc. Instead, we just pass the arguments down. 4. Currently, we need an explicit msanwrite to mark the results struct as written because reflectcall doesn't do this. Now, the results are written by regular Go assignments, so the Go compiler generates the necessary MSAN annotations. This also means we no longer need to track the size of the arguments frame. Updates #40724, since now we don't need to teach cgo about the register ABI or change how it uses reflectcall. Change-Id: I7840489a2597962aeb670e0c1798a16a7359c94f Reviewed-on: https://go-review.googlesource.com/c/go/+/258938 Trust: Austin Clements <austin@google.com> Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
Diffstat (limited to 'src/runtime/asm_amd64.s')
-rw-r--r--src/runtime/asm_amd64.s55
1 files changed, 21 insertions, 34 deletions
diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s
index 256f4112cd..3d5d9c4d58 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -691,25 +691,9 @@ nosave:
MOVL AX, ret+16(FP)
RET
-// func cgocallback(fn, frame unsafe.Pointer, framesize, ctxt uintptr)
-// Turn the fn into a Go func (by taking its address) and call
-// cgocallback_gofunc.
-TEXT runtime·cgocallback(SB),NOSPLIT,$32-32
- LEAQ fn+0(FP), AX
- MOVQ AX, 0(SP)
- MOVQ frame+8(FP), AX
- MOVQ AX, 8(SP)
- MOVQ framesize+16(FP), AX
- MOVQ AX, 16(SP)
- MOVQ ctxt+24(FP), AX
- MOVQ AX, 24(SP)
- MOVQ $runtime·cgocallback_gofunc(SB), AX
- CALL AX
- RET
-
-// func cgocallback_gofunc(fn, frame, framesize, ctxt uintptr)
+// func cgocallback(fn, frame unsafe.Pointer, ctxt uintptr)
// See cgocall.go for more details.
-TEXT ·cgocallback_gofunc(SB),NOSPLIT,$16-32
+TEXT ·cgocallback(SB),NOSPLIT,$32-24
NO_LOCAL_POINTERS
// If g is nil, Go did not create the current thread.
@@ -769,37 +753,40 @@ havem:
// save that information (m->curg->sched) so we can restore it.
// We can restore m->curg->sched.sp easily, because calling
// runtime.cgocallbackg leaves SP unchanged upon return.
- // To save m->curg->sched.pc, we push it onto the stack.
- // This has the added benefit that it looks to the traceback
- // routine like cgocallbackg is going to return to that
- // PC (because the frame we allocate below has the same
- // size as cgocallback_gofunc's frame declared above)
- // so that the traceback will seamlessly trace back into
- // the earlier calls.
+ // To save m->curg->sched.pc, we push it onto the curg stack and
+ // open a frame the same size as cgocallback's g0 frame.
+ // Once we switch to the curg stack, the pushed PC will appear
+ // to be the return PC of cgocallback, so that the traceback
+ // will seamlessly trace back into the earlier calls.
//
- // In the new goroutine, 8(SP) holds the saved R8.
+ // In the new goroutine, 24(SP) holds the saved R8.
MOVQ m_curg(BX), SI
MOVQ SI, g(CX)
MOVQ (g_sched+gobuf_sp)(SI), DI // prepare stack as DI
MOVQ (g_sched+gobuf_pc)(SI), BX
- MOVQ BX, -8(DI)
+ MOVQ BX, -8(DI) // "push" return PC on the g stack
+ // Gather our arguments into registers.
+ MOVQ fn+0(FP), BX
+ MOVQ frame+8(FP), CX
+ MOVQ ctxt+16(FP), DX
// Compute the size of the frame, including return PC and, if
// GOEXPERIMENT=framepointer, the saved base pointer
- MOVQ ctxt+24(FP), BX
- LEAQ fv+0(FP), AX
- SUBQ SP, AX
- SUBQ AX, DI
+ LEAQ fn+0(FP), AX
+ SUBQ SP, AX // AX is our actual frame size
+ SUBQ AX, DI // Allocate the same frame size on the g stack
MOVQ DI, SP
- MOVQ R8, 8(SP)
+ MOVQ R8, 24(SP)
MOVQ BX, 0(SP)
+ MOVQ CX, 8(SP)
+ MOVQ DX, 16(SP)
CALL runtime·cgocallbackg(SB)
- MOVQ 8(SP), R8
+ MOVQ 24(SP), R8
// Compute the size of the frame again. FP and SP have
// completely different values here than they did above,
// but only their difference matters.
- LEAQ fv+0(FP), AX
+ LEAQ fn+0(FP), AX
SUBQ SP, AX
// Restore g->sched (== m->curg->sched) from saved values.