Skip to content

Commit 4d04819

Browse files
committed
runtime: support new callbackasm1 calling convention on windows/arm
This updates the callbacks implementation on windows/arm for the changes made in CL 258938. At the time, that was left as a TODO. At the same time, it also extends the previous support for only 4 arguments to also support additional arguments on the stack. This is required for functions like SetWinEventHook, which take 7 arguments. It does this by pushing r0-r3 onto the stack before the normal prologue, and then pointing the args struct to that location. This is derived from CL 270077 and CL 270078. Updates #40724. Fixes #42591. Change-Id: Icc199e7f2c24205e41be4e00015283c7e2a9b797 Reviewed-on: https://go-review.googlesource.com/c/go/+/271178 Run-TryBot: Jason A. Donenfeld <[email protected]> Trust: Jason A. Donenfeld <[email protected]> Reviewed-by: Austin Clements <[email protected]>
1 parent 5ba1c3f commit 4d04819

File tree

2 files changed

+56
-45
lines changed

2 files changed

+56
-45
lines changed

src/runtime/sys_windows_arm.s

+33-39
Original file line numberDiff line numberDiff line change
@@ -314,48 +314,42 @@ TEXT runtime·externalthreadhandler(SB),NOSPLIT|NOFRAME,$0
314314
GLOBL runtime·cbctxts(SB), NOPTR, $4
315315

316316
TEXT runtime·callbackasm1(SB),NOSPLIT|NOFRAME,$0
317-
// TODO(austin): This needs to be converted to match changes
318-
// in cgocallback, but I have no way to test. See CL 258938,
319-
// and callbackasm1 on amd64 and 386.
320-
MOVM.DB.W [R4-R11, R14], (R13) // push {r4-r11, lr}
321-
SUB $36, R13 // space for locals
322-
323-
// save callback arguments to stack. We currently support up to 4 arguments
324-
ADD $16, R13, R4
325-
MOVM.IA [R0-R3], (R4)
326-
327-
// load cbctxts[i]. The trampoline in zcallback_windows.s puts the callback
328-
// index in R12
329-
MOVW runtime·cbctxts(SB), R4
330-
MOVW R12<<2(R4), R4 // R4 holds pointer to wincallbackcontext structure
331-
332-
// extract callback context
333-
MOVW wincallbackcontext_argsize(R4), R5
334-
MOVW wincallbackcontext_gobody(R4), R4
335-
336-
// we currently support up to 4 arguments
337-
CMP $(4 * 4), R5
338-
BL.GT runtime·abort(SB)
339-
340-
// extend argsize by size of return value
341-
ADD $4, R5
342-
343-
// Build 'type args struct'
344-
MOVW R4, 4(R13) // fn
345-
ADD $16, R13, R0 // arg (points to r0-r3, ret on stack)
346-
MOVW R0, 8(R13)
347-
MOVW R5, 12(R13) // argsize
317+
// On entry, the trampoline in zcallback_windows_arm.s left
318+
// the callback index in R12 (which is volatile in the C ABI).
319+
320+
// Push callback register arguments r0-r3. We do this first so
321+
// they're contiguous with stack arguments.
322+
MOVM.DB.W [R0-R3], (R13)
323+
// Push C callee-save registers r4-r11 and lr.
324+
MOVM.DB.W [R4-R11, R14], (R13)
325+
SUB $(16 + callbackArgs__size), R13 // space for locals
326+
327+
// Create a struct callbackArgs on our stack.
328+
MOVW R12, (16+callbackArgs_index)(R13) // callback index
329+
MOVW $(16+callbackArgs__size+4*9)(R13), R0
330+
MOVW R0, (16+callbackArgs_args)(R13) // address of args vector
331+
MOVW $0, R0
332+
MOVW R0, (16+callbackArgs_result)(R13) // result
348333

334+
// Prepare for entry to Go.
349335
BL runtime·load_g(SB)
350-
BL runtime·cgocallback_gofunc(SB)
351336

352-
ADD $16, R13, R0 // load arg
353-
MOVW 12(R13), R1 // load argsize
354-
SUB $4, R1 // offset to return value
355-
MOVW R1<<0(R0), R0 // load return value
356-
357-
ADD $36, R13 // free locals
358-
MOVM.IA.W (R13), [R4-R11, R15] // pop {r4-r11, pc}
337+
// Call cgocallback, which will call callbackWrap(frame).
338+
MOVW $0, R0
339+
MOVW R0, 12(R13) // context
340+
MOVW $16(R13), R1 // R1 = &callbackArgs{...}
341+
MOVW R1, 8(R13) // frame (address of callbackArgs)
342+
MOVW $·callbackWrap(SB), R1
343+
MOVW R1, 4(R13) // PC of function to call
344+
BL runtime·cgocallback(SB)
345+
346+
// Get callback result.
347+
MOVW (16+callbackArgs_result)(R13), R0
348+
349+
ADD $(16 + callbackArgs__size), R13 // free locals
350+
MOVM.IA.W (R13), [R4-R11, R12] // pop {r4-r11, lr=>r12}
351+
ADD $(4*4), R13 // skip r0-r3
352+
B (R12) // return
359353

360354
// uint32 tstart_stdcall(M *newm);
361355
TEXT runtime·tstart_stdcall(SB),NOSPLIT|NOFRAME,$0

src/runtime/syscall_windows.go

+23-6
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,19 @@ func compileCallback(fn eface, cdecl bool) (code uintptr) {
109109
// passed as two words (little endian); and
110110
// structs are pushed on the stack. In
111111
// fastcall, arguments larger than the word
112-
// size are passed by reference.
112+
// size are passed by reference. On arm,
113+
// 8-byte aligned arguments round up to the
114+
// next even register and can be split across
115+
// registers and the stack.
113116
panic("compileCallback: argument size is larger than uintptr")
114117
}
115-
if k := t.kind & kindMask; GOARCH == "amd64" && (k == kindFloat32 || k == kindFloat64) {
118+
if k := t.kind & kindMask; (GOARCH == "amd64" || GOARCH == "arm") && (k == kindFloat32 || k == kindFloat64) {
116119
// In fastcall, floating-point arguments in
117120
// the first four positions are passed in
118121
// floating-point registers, which we don't
119-
// currently spill.
122+
// currently spill. arm passes floating-point
123+
// arguments in VFP registers, which we also
124+
// don't support.
120125
panic("compileCallback: float arguments not supported")
121126
}
122127

@@ -128,6 +133,7 @@ func compileCallback(fn eface, cdecl bool) (code uintptr) {
128133
// argument word and all supported Windows
129134
// architectures are little endian, so src is already
130135
// pointing to the right place for smaller arguments.
136+
// The same is true on arm.
131137

132138
// Copy just the size of the argument. Note that this
133139
// could be a small by-value struct, but C and Go
@@ -139,7 +145,7 @@ func compileCallback(fn eface, cdecl bool) (code uintptr) {
139145
abiMap = append(abiMap, part)
140146
}
141147

142-
// cdecl, stdcall, and fastcall pad arguments to word size.
148+
// cdecl, stdcall, fastcall, and arm pad arguments to word size.
143149
src += sys.PtrSize
144150
// The Go ABI packs arguments.
145151
dst += t.size
@@ -205,7 +211,18 @@ func compileCallback(fn eface, cdecl bool) (code uintptr) {
205211

206212
type callbackArgs struct {
207213
index uintptr
208-
args unsafe.Pointer // Arguments in stdcall/cdecl convention, with registers spilled
214+
// args points to the argument block.
215+
//
216+
// For cdecl and stdcall, all arguments are on the stack.
217+
//
218+
// For fastcall, the trampoline spills register arguments to
219+
// the reserved spill slots below the stack arguments,
220+
// resulting in a layout equivalent to stdcall.
221+
//
222+
// For arm, the trampoline stores the register arguments just
223+
// below the stack arguments, so again we can treat it as one
224+
// big stack arguments frame.
225+
args unsafe.Pointer
209226
// Below are out-args from callbackWrap
210227
result uintptr
211228
retPop uintptr // For 386 cdecl, how many bytes to pop on return
@@ -216,7 +233,7 @@ func callbackWrap(a *callbackArgs) {
216233
c := cbs.ctxt[a.index]
217234
a.retPop = c.retPop
218235

219-
// Convert from stdcall to Go ABI.
236+
// Convert from C to Go ABI.
220237
var frame [callbackMaxFrame]byte
221238
goArgs := unsafe.Pointer(&frame)
222239
for _, part := range c.abiMap {

0 commit comments

Comments
 (0)