Slice Arguments in Golang Assembly
This short post aims to clarify a detail missing from the Golang assembly documentation, namely how to accept a slice argument to an assembly function.
The answer to this depends on the internal representation of slices, captured
by the reflect.SliceHeader
type.
// SliceHeader is the runtime representation of a slice.
// It cannot be used safely or portably and its representation may
// change in a later release.
// Moreover, the Data field is not sufficient to guarantee the data
// it references will not be garbage collected, so programs must keep
// a separate, correctly typed pointer to the underlying data.
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
Therefore under the hood a Go slice is a pointer with length and capacity
values, and when passed to an assembly function these three fields will be
available. For example, the following Go assembly sums a slice of uint64
values.
#include "textflag.h"
// func Sum(x []uint64) uint64
TEXT ·Sum(SB), NOSPLIT, $0
MOVQ x_ptr+0(FP), DI
MOVQ x_len+8(FP), AX
XORQ R8, R8
loop:
CMPQ AX, $0
JE done
MOVQ (DI), R9
ADDQ R9, R8
ADDQ $8, DI
DECQ AX
JMP loop
done:
MOVQ R8, ret+24(FP)
RET
Note we ignore the capacity field of the slice at 16(FP)
, but we account for
it when we write the return value to ret+24(FP)
.